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.


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