Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / geekos / src / geekos / synch.c
1 /*
2  * Synchronization primitives
3  * Copyright (c) 2001,2004 David H. Hovemeyer <daveho@cs.umd.edu>
4  * $Revision: 1.1 $
5  * 
6  * This is free software.  You are permitted to use,
7  * redistribute, and modify it as specified in the file "COPYING".
8  */
9
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>
16
17 #include <geekos/debug.h>
18
19 /*
20  * NOTES:
21  * - The GeekOS mutex and condition variable APIs are based on those
22  *   in pthreads.
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.
27  */
28
29 /* ----------------------------------------------------------------------
30  * Private functions
31  * ---------------------------------------------------------------------- */
32
33 /*
34  * The mutex is currently locked.
35  * Atomically reenable preemption and wait in the
36  * mutex's wait queue.
37  */
38 static void Mutex_Wait(struct Mutex *mutex)
39 {
40     KASSERT(mutex->state == MUTEX_LOCKED);
41     KASSERT(g_preemptionDisabled);
42
43     Disable_Interrupts();
44     g_preemptionDisabled = false;
45     Wait(&mutex->waitQueue);
46     g_preemptionDisabled = true;
47     Enable_Interrupts();
48 }
49
50 /*
51  * Lock given mutex.
52  * Preemption must be disabled.
53  */
54 static __inline__ void Mutex_Lock_Imp(struct Mutex* mutex)
55 {
56     KASSERT(g_preemptionDisabled);
57
58     /* Make sure we're not already holding the mutex */
59     KASSERT(!IS_HELD(mutex));
60
61     /* Wait until the mutex is in an unlocked state */
62     while (mutex->state == MUTEX_LOCKED) {
63         Mutex_Wait(mutex);
64     }
65
66     /* Now it's ours! */
67     mutex->state = MUTEX_LOCKED;
68     mutex->owner = g_currentThread;
69 }
70
71 /*
72  * Unlock given mutex.
73  * Preemption must be disabled.
74  */
75 static __inline__ void Mutex_Unlock_Imp(struct Mutex* mutex)
76 {
77     KASSERT(g_preemptionDisabled);
78
79     /* Make sure mutex was actually acquired by this thread. */
80     KASSERT(IS_HELD(mutex));
81
82     /* Unlock the mutex. */
83     mutex->state = MUTEX_UNLOCKED;
84     mutex->owner = 0;
85
86     /*
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.
92      */
93     if (!Is_Thread_Queue_Empty(&mutex->waitQueue)) {
94         Disable_Interrupts();
95         Wake_Up_One(&mutex->waitQueue);
96         Enable_Interrupts();
97     }
98 }
99
100 /* ----------------------------------------------------------------------
101  * Public functions
102  * ---------------------------------------------------------------------- */
103
104 /*
105  * Initialize given mutex.
106  */
107 void Mutex_Init(struct Mutex* mutex)
108 {
109     mutex->state = MUTEX_UNLOCKED;
110     mutex->owner = 0;
111     Clear_Thread_Queue(&mutex->waitQueue);
112 }
113
114 /*
115  * Lock given mutex.
116  */
117 void Mutex_Lock(struct Mutex* mutex)
118 {
119     KASSERT(Interrupts_Enabled());
120
121     g_preemptionDisabled = true;
122     Mutex_Lock_Imp(mutex);
123     g_preemptionDisabled = false;
124 }
125
126 /*
127  * Unlock given mutex.
128  */
129 void Mutex_Unlock(struct Mutex* mutex)
130 {
131     KASSERT(Interrupts_Enabled());
132
133     g_preemptionDisabled = true;
134     Mutex_Unlock_Imp(mutex);
135     g_preemptionDisabled = false;
136 }
137
138 /*
139  * Destroy Mutex
140  */
141 void Mutex_Destroy(struct Mutex* mutex)
142 {
143
144
145 }
146
147 /*
148  * Condition Destroy
149  */
150 void Cond_Destroy(struct Condition* cond)
151 {
152
153
154 }
155
156 /*
157  * Initialize given condition.
158  */
159 void Cond_Init(struct Condition* cond)
160 {
161     Clear_Thread_Queue(&cond->waitQueue);
162 }
163
164 /*
165  * Wait on given condition (protected by given mutex).
166  */
167 void Cond_Wait(struct Condition* cond, struct Mutex* mutex)
168 {
169     KASSERT(Interrupts_Enabled());
170
171     /* Ensure mutex is held. */
172     KASSERT(IS_HELD(mutex));
173
174     /* Turn off scheduling. */
175     g_preemptionDisabled = true;
176
177     /*
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.
182      */
183     Mutex_Unlock_Imp(mutex);
184
185     /*
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.
191      */
192     Disable_Interrupts();
193     g_preemptionDisabled = false;
194     Wait(&cond->waitQueue);
195     g_preemptionDisabled = true;
196     Enable_Interrupts();
197
198     /* Reacquire the mutex. */
199     Mutex_Lock_Imp(mutex);
200
201     /* Turn scheduling back on. */
202     g_preemptionDisabled = false;
203 }
204
205
206
207 struct timeout_data {
208   int pid;
209   int timed_out;
210   struct Thread_Queue * waitQueue;
211 };
212
213
214 static void timeout_cb(int id, void * arg) {
215   struct timeout_data * to_state = (struct timeout_data *)arg;
216   
217   to_state->timed_out = 1;
218   Wake_Up_Thread(to_state->waitQueue, to_state->pid);
219
220 }
221
222
223 /*
224  * Wait on given condition (protected by given mutex).
225  */
226 int Cond_Wait_Timeout(struct Condition* cond, struct Mutex* mutex, uint_t ms)
227 {
228   struct timeout_data to_state;
229   struct Kernel_Thread * self = Get_Current();
230
231   to_state.pid = self->pid;
232   to_state.timed_out = 0;
233   to_state.waitQueue = &cond->waitQueue;
234
235     KASSERT(Interrupts_Enabled());
236
237     /* Ensure mutex is held. */
238     KASSERT(IS_HELD(mutex));
239
240     /* Turn off scheduling. */
241     g_preemptionDisabled = true;
242
243
244
245     /*
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.
250      */
251     Mutex_Unlock_Imp(mutex);
252
253
254     Start_Timer_MSecs(ms, timeout_cb, &to_state);
255
256     /*
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.
262      */
263     Disable_Interrupts();
264     g_preemptionDisabled = false;
265     Wait(&cond->waitQueue);
266     g_preemptionDisabled = true;
267     Enable_Interrupts();
268
269     if (to_state.timed_out == 0) {  
270       /* Reacquire the mutex. */
271       Mutex_Lock_Imp(mutex);
272     }
273
274     /* Turn scheduling back on. */
275     g_preemptionDisabled = false;
276
277
278     return to_state.timed_out;
279 }
280
281 /*
282  * Wake up one thread waiting on the given condition.
283  * The mutex guarding the condition should be held!
284  */
285 void Cond_Signal(struct Condition* cond)
286 {
287     KASSERT(Interrupts_Enabled());
288     Disable_Interrupts();  /* prevent scheduling */
289     Wake_Up_One(&cond->waitQueue);
290     Enable_Interrupts();  /* resume scheduling */
291 }
292
293 /*
294  * Wake up all threads waiting on the given condition.
295  * The mutex guarding the condition should be held!
296  */
297 void Cond_Broadcast(struct Condition* cond)
298 {
299     KASSERT(Interrupts_Enabled());
300     Disable_Interrupts();  /* prevent scheduling */
301     Wake_Up(&cond->waitQueue);
302     Enable_Interrupts();  /* resume scheduling */
303 }