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>
19 * - The GeekOS mutex and condition variable APIs are based on those
21 * - Unlike disabling interrupts, mutexes offer NO protection against
22 * concurrent execution of interrupt handlers. Mutexes and
23 * condition variables should only be used from kernel threads,
24 * with interrupts enabled.
27 /* ----------------------------------------------------------------------
29 * ---------------------------------------------------------------------- */
32 * The mutex is currently locked.
33 * Atomically reenable preemption and wait in the
36 static void Mutex_Wait(struct Mutex *mutex)
38 KASSERT(mutex->state == MUTEX_LOCKED);
39 KASSERT(g_preemptionDisabled);
42 g_preemptionDisabled = false;
43 Wait(&mutex->waitQueue);
44 g_preemptionDisabled = true;
50 * Preemption must be disabled.
52 static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
54 KASSERT(g_preemptionDisabled);
56 /* Make sure we're not already holding the mutex */
57 KASSERT(!IS_HELD(mutex));
59 /* Wait until the mutex is in an unlocked state */
60 while (mutex->state == MUTEX_LOCKED) {
65 mutex->state = MUTEX_LOCKED;
66 mutex->owner = g_currentThread;
71 * Preemption must be disabled.
73 static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
75 KASSERT(g_preemptionDisabled);
77 /* Make sure mutex was actually acquired by this thread. */
78 KASSERT(IS_HELD(mutex));
80 /* Unlock the mutex. */
81 mutex->state = MUTEX_UNLOCKED;
85 * If there are threads waiting to acquire the mutex,
86 * wake one of them up. Note that it is legal to inspect
87 * the queue with interrupts enabled because preemption
88 * is disabled, and therefore we know that no thread can
89 * concurrently add itself to the queue.
91 if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
93 Wake_Up_One(&mutex->waitQueue);
98 /* ----------------------------------------------------------------------
100 * ---------------------------------------------------------------------- */
103 * Initialize given mutex.
105 void Mutex_Init(struct Mutex* mutex)
107 mutex->state = MUTEX_UNLOCKED;
109 Clear_Thread_Queue(&mutex->waitQueue);
115 void Mutex_Lock(struct Mutex* mutex)
117 KASSERT(Interrupts_Enabled());
119 g_preemptionDisabled = true;
120 Mutex_Lock_Imp(mutex);
121 g_preemptionDisabled = false;
125 * Unlock given mutex.
127 void Mutex_Unlock(struct Mutex* mutex)
129 KASSERT(Interrupts_Enabled());
131 g_preemptionDisabled = true;
132 Mutex_Unlock_Imp(mutex);
133 g_preemptionDisabled = false;
139 void Mutex_Destroy(struct Mutex* mutex)
148 void Cond_Destroy(struct Condition* cond)
155 * Initialize given condition.
157 void Cond_Init(struct Condition* cond)
159 Clear_Thread_Queue(&cond->waitQueue);
163 * Wait on given condition (protected by given mutex).
165 void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
167 KASSERT(Interrupts_Enabled());
169 /* Ensure mutex is held. */
170 KASSERT(IS_HELD(mutex));
172 /* Turn off scheduling. */
173 g_preemptionDisabled = true;
176 * Release the mutex, but leave preemption disabled.
177 * No other threads will be able to run before this thread
178 * is able to wait. Therefore, this thread will not
179 * miss the eventual notification on the condition.
181 Mutex_Unlock_Imp(mutex);
184 * Atomically reenable preemption and wait in the condition wait queue.
185 * Other threads can run while this thread is waiting,
186 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
187 * to wake up this thread.
188 * On wakeup, disable preemption again.
190 Disable_Interrupts();
191 g_preemptionDisabled = false;
192 Wait(&cond->waitQueue);
193 g_preemptionDisabled = true;
196 /* Reacquire the mutex. */
197 Mutex_Lock_Imp(mutex);
199 /* Turn scheduling back on. */
200 g_preemptionDisabled = false;
205 struct timeout_data {
208 struct Thread_Queue * waitQueue;
212 static void timeout_cb(int id, void * arg) {
213 struct timeout_data * to_state = (struct timeout_data *)arg;
215 to_state->timed_out = 1;
216 Wake_Up_Thread(to_state->waitQueue, to_state->pid);
222 * Wait on given condition (protected by given mutex).
224 int Cond_Wait_Timeout(struct Condition* cond, struct Mutex* mutex, uint_t ms)
226 struct timeout_data to_state;
227 struct Kernel_Thread * self = Get_Current();
229 to_state.pid = self->pid;
230 to_state.timed_out = 0;
231 to_state.waitQueue = &cond->waitQueue;
233 KASSERT(Interrupts_Enabled());
235 /* Ensure mutex is held. */
236 KASSERT(IS_HELD(mutex));
238 /* Turn off scheduling. */
239 g_preemptionDisabled = true;
244 * Release the mutex, but leave preemption disabled.
245 * No other threads will be able to run before this thread
246 * is able to wait. Therefore, this thread will not
247 * miss the eventual notification on the condition.
249 Mutex_Unlock_Imp(mutex);
252 Start_Timer_MSecs(ms, timeout_cb, &to_state);
255 * Atomically reenable preemption and wait in the condition wait queue.
256 * Other threads can run while this thread is waiting,
257 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
258 * to wake up this thread.
259 * On wakeup, disable preemption again.
261 Disable_Interrupts();
262 g_preemptionDisabled = false;
263 Wait(&cond->waitQueue);
264 g_preemptionDisabled = true;
267 if (to_state.timed_out == 0) {
268 /* Reacquire the mutex. */
269 Mutex_Lock_Imp(mutex);
272 /* Turn scheduling back on. */
273 g_preemptionDisabled = false;
276 return to_state.timed_out;
280 * Wake up one thread waiting on the given condition.
281 * The mutex guarding the condition should be held!
283 void Cond_Signal(struct Condition* cond)
285 KASSERT(Interrupts_Enabled());
286 Disable_Interrupts(); /* prevent scheduling */
287 Wake_Up_One(&cond->waitQueue);
288 Enable_Interrupts(); /* resume scheduling */
292 * Wake up all threads waiting on the given condition.
293 * The mutex guarding the condition should be held!
295 void Cond_Broadcast(struct Condition* cond)
297 KASSERT(Interrupts_Enabled());
298 Disable_Interrupts(); /* prevent scheduling */
299 Wake_Up(&cond->waitQueue);
300 Enable_Interrupts(); /* resume scheduling */