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/process/ConditionVariable.h"
21 #include "pedigree/kernel/LockGuard.h"
22 #include "pedigree/kernel/Log.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 
33 ConditionVariable::ConditionVariable() : m_Lock(false), m_Waiters()
34 {
35 }
36 
37 ConditionVariable::~ConditionVariable()
38 {
39  broadcast();
40 }
41 
43 {
44  Time::Timestamp zero = Time::Infinity;
45  return wait(mutex, zero);
46 }
47 
49 ConditionVariable::wait(Mutex &mutex, Time::Timestamp &timeout)
50 {
51  Time::Timestamp startTime = Time::getTimeNanoseconds();
52 
53  if (mutex.getValue())
54  {
55  // Mutex must be acquired.
56  WARNING("ConditionVariable::wait called without a locked mutex");
57  return Result<bool, Error>::withError(MutexNotLocked);
58  }
59 
60  Thread *me = Processor::information().getCurrentThread();
61 
62  m_Lock.acquire();
63  m_Waiters.pushBack(me);
64 
65  void *alarmHandle = nullptr;
66  if (timeout != Time::Infinity)
67  {
68  alarmHandle = Time::addAlarm(timeout);
69  }
70 
71  // Safe now to release the mutex as we're about to sleep.
72  mutex.release();
73 
74  uintptr_t ra = reinterpret_cast<uintptr_t>(__builtin_return_address(0));
75  me->setDebugState(Thread::CondWait, ra);
76  Processor::information().getScheduler().sleep(&m_Lock);
77  me->setDebugState(Thread::None, 0);
78 
79  bool interrupted = me->wasInterrupted();
80 
81  // Woken up by something. Remove any alarm we have pending as we're
82  // finishing our wait now.
83  if (alarmHandle)
84  {
85  Time::removeAlarm(alarmHandle);
86  }
87 
88  me->setInterrupted(false);
89 
90  Error err = NoError;
91 
92  bool r = false;
93  if (interrupted)
94  {
95  // Timeout.
96  err = TimedOut;
97  }
98  else if (me->getUnwindState() != Thread::Continue)
99  {
100  // Thread needs to quit.
101  err = ThreadTerminating;
102  }
103  else
104  {
105  // We just got woken by something! Time to re-check the condition.
107  r = mutex.acquire();
108  if (!r)
109  {
110  err = MutexNotAcquired;
111  }
112  }
113 
114  Time::Timestamp endTime = Time::getTimeNanoseconds();
115 
116  // Update timeout value to suit. We want to be able to make consecutive
117  // calls to wait() without changing the timeout value to allow for wakeups
118  // with an unchanged time limit.
119  if (timeout != Time::Infinity)
120  {
121  Time::Timestamp timeConsumed = endTime - startTime;
122  if (timeConsumed >= timeout)
123  {
124  timeout = 0;
125  }
126  else
127  {
128  timeout -= timeConsumed;
129  }
130  }
131 
132  if (err != NoError)
133  {
134  // Remove us from the waiter list (error condition)
135  // This is important as for things like timeouts we would otherwise
136  // keep the reference in m_Waiters and it would never be cleaned up
137  // until a broadcast() finally happens.
138  LockGuard<Spinlock> guard(m_Lock);
139  for (auto it = m_Waiters.begin(); it != m_Waiters.end();)
140  {
141  if ((*it) == me)
142  {
143  it = m_Waiters.erase(it);
144  }
145  else
146  {
147  ++it;
148  }
149  }
150  return Result<bool, Error>::withError(err);
151  }
152  else
153  {
155  }
156 }
157 
159 {
160  LockGuard<Spinlock> guard(m_Lock);
161 
162  if (!m_Waiters.count())
163  {
164  return;
165  }
166 
167  // Mark the next thread ready.
168  Thread *pThread = m_Waiters.popFront();
169  pThread->getLock().acquire();
170  pThread->setStatus(Thread::Ready);
171  pThread->getLock().release();
172 }
173 
175 {
176  LockGuard<Spinlock> guard(m_Lock);
177 
178  for (auto pThread : m_Waiters)
179  {
180  pThread->getLock().acquire();
181  pThread->setStatus(Thread::Ready);
182  pThread->getLock().release();
183  }
184 
185  m_Waiters.clear();
186 }
bool wasInterrupted()
Definition: Thread.h:229
void release()
Definition: Spinlock.cc:273
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition: Semaphore.h:62
void setDebugState(DebugState state, uintptr_t address)
Definition: Thread.h:294
Definition: Mutex.h:58
bool acquire(bool recurse=false, bool safe=true)
Definition: Spinlock.cc:43
Definition: Result.h:36
static ProcessorInformation & information()
Definition: Processor.cc:45
#define WARNING(text)
Definition: Log.h:78
ssize_t getValue()
Definition: Semaphore.cc:318
void release(size_t n=1)
Definition: Semaphore.cc:239
Spinlock & getLock()
Definition: Thread.h:301
void setStatus(Status s)
Definition: Thread.cc:364
No unwind necessary, carry on as normal.
Definition: Thread.h:243
Definition: Thread.h:54
void setInterrupted(bool b)
Definition: Thread.h:235
Definition: errors.h:24
UnwindType getUnwindState()
Definition: Thread.h:261
WaitResult wait(Mutex &mutex, Time::Timestamp &timeout)