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;
137 * Initialize given condition.
139 void Cond_Init(struct Condition* cond)
141 Clear_Thread_Queue(&cond->waitQueue);
145 * Wait on given condition (protected by given mutex).
147 void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
149 KASSERT(Interrupts_Enabled());
151 /* Ensure mutex is held. */
152 KASSERT(IS_HELD(mutex));
154 /* Turn off scheduling. */
155 g_preemptionDisabled = true;
158 * Release the mutex, but leave preemption disabled.
159 * No other threads will be able to run before this thread
160 * is able to wait. Therefore, this thread will not
161 * miss the eventual notification on the condition.
163 Mutex_Unlock_Imp(mutex);
166 * Atomically reenable preemption and wait in the condition wait queue.
167 * Other threads can run while this thread is waiting,
168 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
169 * to wake up this thread.
170 * On wakeup, disable preemption again.
172 Disable_Interrupts();
173 g_preemptionDisabled = false;
174 Wait(&cond->waitQueue);
175 g_preemptionDisabled = true;
178 /* Reacquire the mutex. */
179 Mutex_Lock_Imp(mutex);
181 /* Turn scheduling back on. */
182 g_preemptionDisabled = false;
187 struct timeout_data {
190 struct Thread_Queue * waitQueue;
194 static void timeout_cb(int id, void * arg) {
195 struct timeout_data * to_state = (struct timeout_data *)arg;
197 to_state->timed_out = 1;
198 Wake_Up_Thread(to_state->waitQueue, to_state->pid);
204 * Wait on given condition (protected by given mutex).
206 int Cond_Wait_Timeout(struct Condition* cond, struct Mutex* mutex, uint_t ms)
208 struct timeout_data to_state;
209 struct Kernel_Thread * self = Get_Current();
211 to_state.pid = self->pid;
212 to_state.timed_out = 0;
213 to_state.waitQueue = &cond->waitQueue;
215 KASSERT(Interrupts_Enabled());
217 /* Ensure mutex is held. */
218 KASSERT(IS_HELD(mutex));
220 /* Turn off scheduling. */
221 g_preemptionDisabled = true;
226 * Release the mutex, but leave preemption disabled.
227 * No other threads will be able to run before this thread
228 * is able to wait. Therefore, this thread will not
229 * miss the eventual notification on the condition.
231 Mutex_Unlock_Imp(mutex);
234 Start_Timer_MSecs(ms, timeout_cb, &to_state);
237 * Atomically reenable preemption and wait in the condition wait queue.
238 * Other threads can run while this thread is waiting,
239 * and eventually one of them will call Cond_Signal() or Cond_Broadcast()
240 * to wake up this thread.
241 * On wakeup, disable preemption again.
243 Disable_Interrupts();
244 g_preemptionDisabled = false;
245 Wait(&cond->waitQueue);
246 g_preemptionDisabled = true;
249 if (to_state.timed_out == 0) {
250 /* Reacquire the mutex. */
251 Mutex_Lock_Imp(mutex);
254 /* Turn scheduling back on. */
255 g_preemptionDisabled = false;
258 return to_state.timed_out;
262 * Wake up one thread waiting on the given condition.
263 * The mutex guarding the condition should be held!
265 void Cond_Signal(struct Condition* cond)
267 KASSERT(Interrupts_Enabled());
268 Disable_Interrupts(); /* prevent scheduling */
269 Wake_Up_One(&cond->waitQueue);
270 Enable_Interrupts(); /* resume scheduling */
274 * Wake up all threads waiting on the given condition.
275 * The mutex guarding the condition should be held!
277 void Cond_Broadcast(struct Condition* cond)
279 KASSERT(Interrupts_Enabled());
280 Disable_Interrupts(); /* prevent scheduling */
281 Wake_Up(&cond->waitQueue);
282 Enable_Interrupts(); /* resume scheduling */