The Pedigree Project  0.1
Semaphore.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 
24 #ifdef THREADS
25 
26 #include "pedigree/kernel/process/Semaphore.h"
27 #include "pedigree/kernel/Log.h"
28 #include "pedigree/kernel/machine/Machine.h"
29 #include "pedigree/kernel/machine/Timer.h"
30 #include "pedigree/kernel/process/PerProcessorScheduler.h"
31 #include "pedigree/kernel/process/Scheduler.h"
32 #include "pedigree/kernel/process/Thread.h"
33 #include "pedigree/kernel/process/eventNumbers.h"
34 #include "pedigree/kernel/processor/Processor.h"
35 #include "pedigree/kernel/processor/ProcessorInformation.h"
36 #include "pedigree/kernel/utilities/Iterator.h"
37 #include "pedigree/kernel/utilities/assert.h"
38 #include "pedigree/kernel/utilities/utility.h"
39 
40 static void interruptSemaphore(uint8_t *pBuffer)
41 {
42  Processor::information().getCurrentThread()->setInterrupted(true);
43 }
44 
45 Semaphore::SemaphoreEvent::SemaphoreEvent()
46  : Event(
47  reinterpret_cast<uintptr_t>(&interruptSemaphore),
48  false /* Not deletable */)
49 {
50 }
51 
52 Semaphore::SemaphoreEvent::~SemaphoreEvent() = default;
53 
54 size_t Semaphore::SemaphoreEvent::serialize(uint8_t *pBuffer)
55 {
56  return 0;
57 }
58 
59 bool Semaphore::SemaphoreEvent::unserialize(
60  uint8_t *pBuffer, Semaphore::SemaphoreEvent &event)
61 {
62  return true;
63 }
64 
66 {
67  return EventNumbers::Interrupt;
68 }
69 
70 Semaphore::Semaphore(size_t nInitialValue, bool canInterrupt)
71  : magic(0xdeadbaba), m_Counter(nInitialValue), m_BeingModified(false),
72  m_Queue(), m_bCanInterrupt(canInterrupt)
73 {
74  assert(magic == 0xdeadbaba);
75 }
76 
78 {
79  assert(magic == 0xdeadbaba);
80  m_Queue.clear();
81 }
82 
84 {
85  m_BeingModified.acquire();
86  for (List<Thread *>::Iterator it = m_Queue.begin(); it != m_Queue.end();
87  ++it)
88  {
89  if ((*it) == pThread)
90  {
91  m_Queue.erase(it);
92  break;
93  }
94  }
95  m_BeingModified.release();
96 }
97 
99 Semaphore::acquireWithResult(size_t n, size_t timeoutSecs, size_t timeoutUsecs)
100 {
101  if (magic != 0xdeadbaba)
102  {
103  NOTICE(magic);
104  assert(false);
105  }
106 // Spin 10 times in the case that the lock is about to be released on
107 // multiprocessor systems, and just once for uniprocessor systems, so we don't
108 // go through the rigmarole of creating a timeout event if the lock is
109 // available.
110 #ifdef MULTIPROCESSOR
111  for (int i = 0; i < 10; i++)
112 #endif
113  if (tryAcquire(n))
114  return SemaphoreResult::withValue(true);
115 
116  // If we have a timeout, create the event and register it.
117  Event *pEvent = 0;
118  if (timeoutSecs || timeoutUsecs)
119  {
120  pEvent = new SemaphoreEvent();
121  Machine::instance().getTimer()->addAlarm(
122  pEvent, timeoutSecs, timeoutUsecs);
123  }
124 
125  SemaphoreResult result = SemaphoreResult::withValue(true);
126  while (true)
127  {
128  Thread *pThread = Processor::information().getCurrentThread();
129  if (tryAcquire(n))
130  {
131  if (pEvent)
132  {
133  Machine::instance().getTimer()->removeAlarm(pEvent);
134  delete pEvent;
135  }
136 
137  removeThread(pThread);
138  return result;
139  }
140 
141  m_BeingModified.acquire();
142  bool bWasInterrupts = m_BeingModified.interrupts();
143 
144  // To avoid a race condition, check again here after we've disabled
145  // interrupts. This stops the condition where the lock is released after
146  // tryAcquire returns false, but before we grab the "being modified"
147  // lock, which means the lock could be released by this point!
148  if (tryAcquire(n))
149  {
150  if (pEvent)
151  {
152  Machine::instance().getTimer()->removeAlarm(pEvent);
153  delete pEvent;
154  }
155  m_BeingModified.release();
156  removeThread(pThread);
157  return result;
158  }
159 
160  m_Queue.pushBack(pThread);
161 
162  Thread::WakeReason wakeReason = Thread::NotWoken;
163 
164  pThread->setInterrupted(false);
166  pThread->setDebugState(
167  Thread::SemWait,
168  reinterpret_cast<uintptr_t>(__builtin_return_address(0)));
169  pThread->addWakeupWatcher(&wakeReason);
170  Processor::information().getScheduler().sleep(&m_BeingModified);
171  pThread->setDebugState(Thread::None, 0);
172  pThread->removeWakeupWatcher(&wakeReason); // sanity removal
173 
174  // Either acquired or interrupted, either way, we don't need to be woken
175  // again
176  removeThread(pThread);
177 
178  // Why were we woken?
179  bool bState = true;
180  if (m_bCanInterrupt && wakeReason != Thread::NotWoken)
181  {
182  // don't care about unknown wakeups for this
183  if (wakeReason != Thread::Unknown)
184  {
185  if (pThread->wasInterrupted())
186  {
187  // Timed out!
188  result = SemaphoreResult::withError(TimedOut);
189  }
190  else
191  {
192  // Interrupted by some other source (e.g. an event).
193  result = SemaphoreResult::withError(Interrupted);
194  }
195 
196  if (pEvent)
197  {
198  Machine::instance().getTimer()->removeAlarm(pEvent);
199  delete pEvent;
200  }
201 
202  bState = false;
203  }
204  }
205 
206  // Restore interrupt state. If we are woken by an event (eg, timeout,
207  // user input, etc), we seem to fail to restore the correct interrupt
208  // state when we unwind the event state. This is not great - it leaves
209  // us in a weird state where interrupts are disabled, and means any
210  // further iterations of the acquire loop will believe that interrupts
211  // were entirely disabled. A side-effect of acquiring a Semaphore should
212  // not be that interrupts are disabled, unless they were disabled before
213  // acquire() was called!
214  Processor::setInterrupts(bWasInterrupts);
215 
216  if (!bState)
217  {
218  return result;
219  }
220  }
221 }
222 
223 bool Semaphore::tryAcquire(size_t n)
224 {
225  ssize_t value = m_Counter;
226 
227  if ((value - static_cast<ssize_t>(n)) < 0)
228  return false;
229  if (m_Counter.compareAndSwap(value, value - n))
230  {
231 #ifdef STRICT_LOCK_ORDERING
232 // TODO LockManager::acquired(*this);
233 #endif
234  return true;
235  }
236  return false;
237 }
238 
239 void Semaphore::release(size_t n)
240 {
241  assert(magic == 0xdeadbaba);
242  m_Counter += n;
243 
244  m_BeingModified.acquire();
245  if (m_Queue.count())
246  {
247  // Because we can't wake a suspended thread, and we pop each thread from
248  // the queue, we need to push them back on the queue later.
249  List<Thread *> stillPendingThreads;
250 
251  // We should wake threads without holding the modification lock, so this
252  // list holds the threads that we need to wake.
253  List<Thread *> wakeupThreads;
254 
255  while (m_Queue.count() != 0)
256  {
257  Thread *pThread = m_Queue.popFront();
258  if (!pThread)
259  {
260  WARNING("Null thread in a Semaphore thread queue");
261  continue;
262  }
263  else if (!Scheduler::instance().threadInSchedule(pThread))
264  {
265  WARNING("A thread that was to be woken by a Semaphore is no "
266  "longer in the scheduler");
267  continue;
268  }
269  else if (pThread->getStatus() != Thread::Sleeping)
270  {
271  // Don't wake up a thread that isn't sleeping (perhaps
272  // suspended?)
273  if (pThread->getStatus() == Thread::Zombie)
274  WARNING(
275  "Semaphore has a zombie thread in its thread queue");
276  else
277  stillPendingThreads.pushBack(pThread);
278  continue;
279  }
280 
281  wakeupThreads.pushBack(pThread);
282  }
283 
284  if (stillPendingThreads.count())
285  {
286  for (List<Thread *>::Iterator it = stillPendingThreads.begin();
287  it != stillPendingThreads.end(); ++it)
288  {
289  Thread *pThread = *it;
290  m_Queue.pushBack(pThread);
291  }
292  }
293 
294  m_BeingModified.release();
295 
296  if (wakeupThreads.count())
297  {
298  for (List<Thread *>::Iterator it = wakeupThreads.begin();
299  it != wakeupThreads.end(); ++it)
300  {
301  Thread *pThread = *it;
302  pThread->getLock().acquire();
303  pThread->setStatus(Thread::Ready);
304  pThread->getLock().release();
305  }
306  }
307  }
308  else
309  {
310  m_BeingModified.release();
311  }
312 
313 #ifdef STRICT_LOCK_ORDERING
314 // TODO LockManager::released(*this);
315 #endif
316 }
317 
319 {
320  return static_cast<ssize_t>(m_Counter);
321 }
322 
323 #endif
void removeWakeupWatcher(WakeReason *watcher)
Definition: Thread.cc:995
bool wasInterrupted()
Definition: Thread.h:229
void release()
Definition: Spinlock.cc:273
void clear()
Definition: List.h:386
void addWakeupWatcher(WakeReason *watcher)
Definition: Thread.cc:988
void pushBack(const T &value)
Definition: List.h:232
virtual void removeAlarm(class Event *pEvent)=0
Iterator erase(Iterator &Iter)
Definition: List.h:343
virtual Timer * getTimer()=0
void setDebugState(DebugState state, uintptr_t address)
Definition: Thread.h:294
void removeThread(class Thread *pThread)
Definition: Semaphore.cc:83
T popFront()
Definition: List.h:319
WakeReason
Definition: Thread.h:82
void setUnwindState(UnwindType ut)
Definition: Thread.h:266
bool acquire(bool recurse=false, bool safe=true)
Definition: Spinlock.cc:43
Definition: Result.h:36
static ProcessorInformation & information()
Definition: Processor.cc:45
virtual size_t serialize(uint8_t *pBuffer)
Definition: Semaphore.cc:54
#define WARNING(text)
Definition: Log.h:78
Definition: List.h:64
ssize_t getValue()
Definition: Semaphore.cc:318
Semaphore(size_t nInitialValue, bool canInterrupt=true)
Definition: Semaphore.cc:70
void release(size_t n=1)
Definition: Semaphore.cc:239
#define NOTICE(text)
Definition: Log.h:74
Spinlock & getLock()
Definition: Thread.h:301
#define assert(x)
Definition: assert.h:37
Iterator begin()
Definition: List.h:123
static Scheduler & instance()
Definition: Scheduler.h:48
virtual ~Semaphore()
Definition: Semaphore.cc:77
static void setInterrupts(bool bEnable)
void setStatus(Status s)
Definition: Thread.cc:364
SemaphoreResult acquireWithResult(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition: Semaphore.cc:99
Status getStatus() const
Definition: Thread.h:192
No unwind necessary, carry on as normal.
Definition: Thread.h:243
Definition: Thread.h:54
Definition: Event.h:48
void setInterrupted(bool b)
Definition: Thread.h:235
bool tryAcquire(size_t n=1)
Definition: Semaphore.cc:223
virtual void addAlarm(class Event *pEvent, size_t alarmSecs, size_t alarmUsecs=0)=0
virtual size_t getNumber()
Definition: Semaphore.cc:65
Iterator end()
Definition: List.h:135
size_t count() const
Definition: List.h:227