2 * Synchronization primitives
3 * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
6 * This is free software. You are permitted to use,
7 * redistribute, and modify it as specified in the file "COPYING".
10 #include <geekos/kthread.h>
11 #include <geekos/int.h>
12 #include <geekos/kassert.h>
13 #include <geekos/screen.h>
14 #include <geekos/synch.h>
15 #include <geekos/timer.h>
17 #include <geekos/debug.h>
21 * - The GeekOS mutex and condition variable APIs are based on those
23 * - Unlike disabling interrupts, mutexes offer NO protection against
24 * concurrent execution of interrupt handlers. Mutexes and
25 * condition variables should only be used from kernel threads,
26 * with interrupts enabled.
29 /* ----------------------------------------------------------------------
31 * ---------------------------------------------------------------------- */
34 * The mutex is currently locked.
35 * Atomically reenable preemption and wait in the
38 static void Mutex_Wait(struct Mutex *mutex)
40 KASSERT(mutex->state == MUTEX_LOCKED);
41 KASSERT(g_preemptionDisabled);
44 g_preemptionDisabled = false;
45 Wait(&mutex->waitQueue);
46 g_preemptionDisabled = true;
52 * Preemption must be disabled.
54 static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
56 KASSERT(g_preemptionDisabled);
58 /* Make sure we're not already holding the mutex */
59 KASSERT(!IS_HELD(mutex));
61 /* Wait until the mutex is in an unlocked state */
62 while (mutex->state == MUTEX_LOCKED) {
67 mutex->state = MUTEX_LOCKED;
68 mutex->owner = g_currentThread;
73 * Preemption must be disabled.
75 static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
77 KASSERT(g_preemptionDisabled);
79 /* Make sure mutex was actually acquired by this thread. */
80 KASSERT(IS_HELD(mutex));
82 /* Unlock the mutex. */
83 mutex->state = MUTEX_UNLOCKED;
87 * If there are threads waiting to acquire the mutex,
88 * wake one of them up. Note that it is legal to inspect
89 * the queue with interrupts enabled because preemption
90 * is disabled, and therefore we know that no thread can
91 * concurrently add itself to the queue.
93 if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
95 Wake_Up_One(&mutex->waitQueue);
100 /* ----------------------------------------------------------------------
102 * ---------------------------------------------------------------------- */
105 * Initialize given mutex.
107 void Mutex_Init(struct Mutex* mutex)
109 mutex->state = MUTEX_UNLOCKED;
111 Clear_Thread_Queue(&mutex->waitQueue);
117 void Mutex_Lock(struct Mutex* mutex)
119 KASSERT(Interrupts_Enabled());
121 g_preemptionDisabled = true;
122 Mutex_Lock_Imp(mutex);
123 g_preemptionDisabled = false;
127 * Unlock given mutex.
129 void Mutex_Unlock(struct Mutex* mutex)
131 KASSERT(Interrupts_Enabled());
133 g_preemptionDisabled = true;
134 Mutex_Unlock_Imp(mutex);
135 g_preemptionDisabled = false;
141 void Mutex_Destroy(struct Mutex* mutex)
150 void Cond_Destroy(struct Condition* cond)
157 * Initialize given condition.
159 void Cond_Init(struct Condition* cond)
161 Clear_Thread_Queue(&cond->waitQueue);
165 * Wait on given condition (protected by given mutex).
167 void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
169 KASSERT(Interrupts_Enabled());
171 /* Ensure mutex is held. */
172 KASSERT(IS_HELD(mutex));
174 /* Turn off scheduling. */
175 g_preemptionDisabled = true;
178 * Release the mutex, but leave preemption disabled.
179 * No other threads will be able to run before this thread
180 * is able to wait. Therefore, this thread will not
181 * miss the eventual notification on the condition.
183 Mutex_Unlock_Imp(mutex);
186 * Atomically reenable preemption and wait in the condition wait queue.
187 * Other threads can run while this thread is waiting,
188 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
189 * to wake up this thread.
190 * On wakeup, disable preemption again.
192 Disable_Interrupts();
193 g_preemptionDisabled = false;
194 Wait(&cond->waitQueue);
195 g_preemptionDisabled = true;
198 /* Reacquire the mutex. */
199 Mutex_Lock_Imp(mutex);
201 /* Turn scheduling back on. */
202 g_preemptionDisabled = false;
207 struct timeout_data {
210 struct Thread_Queue * waitQueue;
214 static void timeout_cb(int id, void * arg) {
215 struct timeout_data * to_state = (struct timeout_data *)arg;
217 to_state->timed_out = 1;
218 Wake_Up_Thread(to_state->waitQueue, to_state->pid);
224 * Wait on given condition (protected by given mutex).
226 int Cond_Wait_Timeout(struct Condition* cond, struct Mutex* mutex, uint_t ms)
228 struct timeout_data to_state;
229 struct Kernel_Thread * self = Get_Current();
231 to_state.pid = self->pid;
232 to_state.timed_out = 0;
233 to_state.waitQueue = &cond->waitQueue;
235 KASSERT(Interrupts_Enabled());
237 /* Ensure mutex is held. */
238 KASSERT(IS_HELD(mutex));
240 /* Turn off scheduling. */
241 g_preemptionDisabled = true;
246 * Release the mutex, but leave preemption disabled.
247 * No other threads will be able to run before this thread
248 * is able to wait. Therefore, this thread will not
249 * miss the eventual notification on the condition.
251 Mutex_Unlock_Imp(mutex);
254 Start_Timer_MSecs(ms, timeout_cb, &to_state);
257 * Atomically reenable preemption and wait in the condition wait queue.
258 * Other threads can run while this thread is waiting,
259 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
260 * to wake up this thread.
261 * On wakeup, disable preemption again.
263 Disable_Interrupts();
264 g_preemptionDisabled = false;
265 Wait(&cond->waitQueue);
266 g_preemptionDisabled = true;
269 if (to_state.timed_out == 0) {
270 /* Reacquire the mutex. */
271 Mutex_Lock_Imp(mutex);
274 /* Turn scheduling back on. */
275 g_preemptionDisabled = false;
278 return to_state.timed_out;
282 * Wake up one thread waiting on the given condition.
283 * The mutex guarding the condition should be held!
285 void Cond_Signal(struct Condition* cond)
287 KASSERT(Interrupts_Enabled());
288 Disable_Interrupts(); /* prevent scheduling */
289 Wake_Up_One(&cond->waitQueue);
290 Enable_Interrupts(); /* resume scheduling */
294 * Wake up all threads waiting on the given condition.
295 * The mutex guarding the condition should be held!
297 void Cond_Broadcast(struct Condition* cond)
299 KASSERT(Interrupts_Enabled());
300 Disable_Interrupts(); /* prevent scheduling */
301 Wake_Up(&cond->waitQueue);
302 Enable_Interrupts(); /* resume scheduling */