The Pedigree Project 0.1
ConditionVariable.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/process/ConditionVariable.h"
23#include "pedigree/kernel/process/Mutex.h"
24#include "pedigree/kernel/process/PerProcessorScheduler.h"
25#include "pedigree/kernel/process/Thread.h"
26#include "pedigree/kernel/processor/Processor.h"
27#include "pedigree/kernel/processor/ProcessorInformation.h"
28#include "pedigree/kernel/processor/types.h"
29#include "pedigree/kernel/time/Time.h"
30#include "pedigree/kernel/utilities/Iterator.h"
31#include "pedigree/kernel/utilities/utility.h"
32
33namespace {
34struct ConditionStackDiscard {
35 void* alarmHandle;
36 WaitQueue::StackDiscardCleanup caller;
37 void* callerContext;
38};
39
40void discardConditionWait(void* context) {
41 ConditionStackDiscard* discard = reinterpret_cast<ConditionStackDiscard*>(context);
42 void* alarmHandle = discard->alarmHandle;
43 WaitQueue::StackDiscardCleanup caller = discard->caller;
44 void* callerContext = discard->callerContext;
45 discard->alarmHandle = nullptr;
46 discard->caller = nullptr;
47 discard->callerContext = nullptr;
48
49 if (alarmHandle) {
50 Time::removeAlarm(alarmHandle);
51 }
52 if (caller) {
53 caller(callerContext);
54 }
55}
56} // namespace
57
58ConditionVariable::ConditionVariable() : m_Waiters() {}
59
60ConditionVariable::~ConditionVariable() {}
61
62bool ConditionVariable::wait(Mutex& mutex, Error& error,
63 WaitQueue::StackDiscardCleanup onStackDiscard,
64 void* stackDiscardContext) {
65 Time::Timestamp timeout = Time::Infinity;
66 return wait(mutex, timeout, error, onStackDiscard, stackDiscardContext);
67}
68
69bool ConditionVariable::wait(Mutex& mutex, Time::Timestamp& timeout, Error& error,
70 WaitQueue::StackDiscardCleanup onStackDiscard,
71 void* stackDiscardContext) {
72 error = NoError;
73 Time::Timestamp startTime = Time::getTicks();
74
75 if (!mutex.isOwnedByCurrentThread()) {
76 WARNING("ConditionVariable::wait called without owning its mutex");
77 error = MutexNotLocked;
78 return false;
79 }
80
81 if (timeout == 0) {
82 error = TimedOut;
83 return false;
84 }
85
86 Thread* me = Processor::information().getCurrentThread();
87 me->clearInterruption();
88
89 auto guard = m_Waiters.acquire();
90
91 ConditionStackDiscard discard = {nullptr, onStackDiscard, stackDiscardContext};
92 if (timeout != Time::Infinity) {
93 discard.alarmHandle = Time::addAlarm(timeout);
94 }
95 Thread::StackDiscardScope discardScope(
96 (discard.alarmHandle || onStackDiscard) ? &discardConditionWait : nullptr, &discard);
97
98 uintptr_t ra = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
99 WaitQueue::WakeReason wakeReason =
100 guard.waitAndUnlock(mutex, WaitQueue::Channel(), Thread::CondWait, ra);
101
102 // Event delivery can follow an ordinary wake which already won
103 // waiter.reason. The per-wait marker remains authoritative.
104 const Thread::InterruptionReason interruption = me->getInterruptionReason();
105
106 // Woken up by something. Remove any alarm we have pending as we're
107 // finishing our wait now.
108 if (discard.alarmHandle) {
109 void* alarmHandle = discard.alarmHandle;
110 discard.alarmHandle = nullptr;
111 Time::removeAlarm(alarmHandle);
112 }
113
114 me->clearInterruption();
115
116 if (interruption == Thread::InterruptedByTimeout) {
117 error = TimedOut;
118 } else if (interruption == Thread::InterruptedBySignal) {
119 error = Interrupted;
120 } else if (wakeReason == WaitQueue::WakeReason::Unwinding ||
121 wakeReason == WaitQueue::WakeReason::Terminating) {
122 // WaitQueue has already reacquired mutex, so callers can retire
123 // stack-owned state while propagating the terminal request.
124 error = TerminationDeferred;
125 }
126
127 Time::Timestamp endTime = Time::getTicks();
128
129 // Update timeout value to suit. We want to be able to make consecutive
130 // calls to wait() without changing the timeout value to allow for wakeups
131 // with an unchanged time limit.
132 if (timeout != Time::Infinity) {
133 Time::Timestamp timeConsumed = endTime - startTime;
134 if (timeConsumed >= timeout) {
135 timeout = 0;
136 } else {
137 timeout -= timeConsumed;
138 }
139 }
140
141 return error == NoError;
142}
143
145 if (!mutex.isOwnedByCurrentThread()) {
146 FATAL(
147 "ConditionVariable::waitForCompletion called without owning "
148 "its mutex");
149 }
150
151 auto guard = m_Waiters.acquire();
152 uintptr_t ra = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
153 const WaitQueue::WakeReason reason =
154 guard.waitAndUnlockForCompletion(mutex, WaitQueue::Channel(), Thread::CondWait, ra);
155 (void)reason;
156}
157
159 m_Waiters.wakeOne();
160}
161
163 m_Waiters.wakeAll();
164}
void waitForCompletion(Mutex &mutex)
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
Definition Mutex.h:56
static ProcessorInformation & information()
MUST_USE_RESULT WakeReason waitAndUnlockForCompletion(Mutex &mutex, const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0)
Definition WaitQueue.cc:151