Initial version of public stable barrelfish repository.
[barrelfish] / lib / barrelfish / thread_sync.c
1 /**
2  * \file
3  * \brief Thread synchronisation primitives.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/dispatch.h>
17 #include <barrelfish/dispatcher_arch.h>
18 #include <trace/trace.h>
19 #include <threads.h>
20
21 #ifndef TRACE_THREADS
22 #define trace_event(a,b,c) ((void)0)
23 #endif
24
25
26 /**
27  * \brief Initialise a condition variable
28  *
29  * \param cond Condition variable pointer
30  */
31 void thread_cond_init(struct thread_cond *cond)
32 {
33     cond->queue = NULL;
34     cond->lock = 0;
35 }
36
37 /**
38  * \brief Wait for a condition variable
39  *
40  * This function waits for the given condition variable to be signalled
41  * (through thread_cond_signal() or thread_cond_broadcast()) before
42  * returning, while atomically unlocking the mutex pointed to by
43  * 'mutex'.
44  *
45  * \param cond  Condition variable pointer
46  * \param mutex Optional pointer to mutex to unlock.
47  */
48 void thread_cond_wait(struct thread_cond *cond, struct thread_mutex *mutex)
49 {
50     dispatcher_handle_t disp = disp_disable();
51
52     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_WAIT_ENTER,
53                 (uintptr_t)cond);
54
55     acquire_spinlock(&cond->lock);
56
57     // Release the lock
58     if (mutex != NULL) {
59         struct thread *wakeup = thread_mutex_unlock_disabled(disp, mutex);
60
61         if(wakeup != NULL) {
62             errval_t err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
63             assert_disabled(err_is_ok(err));
64         }
65     }
66
67     // Block on the condition variable and release spinlock
68     thread_block_and_release_spinlock_disabled(disp, &cond->queue, &cond->lock);
69
70     // Re-acquire the mutex
71     if (mutex != NULL) {
72         thread_mutex_lock(mutex);
73     }
74
75     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_WAIT_LEAVE,
76                 (uintptr_t)cond);
77 }
78
79 /**
80  * \brief Signal a condition variable
81  *
82  * This function signals the condition variable, and wakes up one
83  * thread waiting on it.
84  *
85  * \param cond Condition variable pointer
86  */
87 void thread_cond_signal(struct thread_cond *cond)
88 {
89     struct thread *wakeup = NULL;
90     errval_t err = SYS_ERR_OK;
91
92     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_SIGNAL,
93                 (uintptr_t)cond);
94
95     // Wakeup one waiting thread
96     dispatcher_handle_t disp = disp_disable();
97     acquire_spinlock(&cond->lock);
98     if (cond->queue != NULL) {
99         wakeup = thread_unblock_one_disabled(disp, &cond->queue, NULL);
100         if(wakeup != NULL) {
101             err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
102         }
103     }
104     release_spinlock(&cond->lock);
105     disp_enable(disp);
106
107     if(err_is_fail(err)) {
108         USER_PANIC_ERR(err, "remote wakeup from condition signal");
109     }
110
111     if(wakeup != NULL) {
112         // XXX: Need directed yield to inter-disp thread
113         thread_yield();
114     }
115 }
116
117 /**
118  * \brief Broadcast signal a condition variable
119  *
120  * This function signals the condition variable, and wakes up all
121  * threads waiting on it.
122  *
123  * \param cond Condition variable pointer
124  */
125 void thread_cond_broadcast(struct thread_cond *cond)
126 {
127     struct thread *wakeupq = NULL;
128     bool foreignwakeup = false;
129
130     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_BROADCAST,
131                 (uintptr_t)cond);
132
133     // Wakeup all waiting threads
134     dispatcher_handle_t disp = disp_disable();
135     acquire_spinlock(&cond->lock);
136     wakeupq = thread_unblock_all_disabled(disp, &cond->queue, NULL);
137     release_spinlock(&cond->lock);
138     disp_enable(disp);
139
140     foreignwakeup = (wakeupq != NULL);
141     // Now, wakeup all on foreign dispatchers
142     while (wakeupq != NULL) {
143         struct thread *wakeup = wakeupq;
144         wakeupq = wakeupq->next;
145         errval_t err = domain_wakeup_on(wakeup->disp, wakeup);
146         if(err_is_fail(err)) {
147             USER_PANIC_ERR(err, "remote wakeup from condition broadcast");
148         }
149     }
150
151     if(foreignwakeup) {
152         // XXX: Need directed yield to inter-disp thread
153         thread_yield();
154     }
155 }
156
157 /**
158  * \brief Initialise a mutex
159  *
160  * \param mutex Mutex pointer
161  */
162 void thread_mutex_init(struct thread_mutex *mutex)
163 {
164     mutex->locked = 0;
165     mutex->holder = NULL;
166     mutex->queue = NULL;
167     mutex->lock = 0;
168 }
169
170 /**
171  * \brief Lock a mutex
172  *
173  * This blocks until the given mutex is unlocked, and then atomically locks it.
174  *
175  * \param mutex Mutex pointer
176  */
177 void thread_mutex_lock(struct thread_mutex *mutex)
178 {
179     dispatcher_handle_t handle = disp_disable();
180     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
181
182     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_ENTER,
183                 (uintptr_t)mutex);
184
185     acquire_spinlock(&mutex->lock);
186     if (mutex->locked > 0) {
187         thread_block_and_release_spinlock_disabled(handle, &mutex->queue,
188                                                    &mutex->lock);
189     } else {
190         mutex->locked = 1;
191         mutex->holder = disp_gen->current;
192         release_spinlock(&mutex->lock);
193         disp_enable(handle);
194     }
195
196     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_LEAVE,
197                 (uintptr_t)mutex);
198 }
199
200 /**
201  * \brief Lock a mutex
202  *
203  * This blocks until the given mutex is unlocked, and then atomically locks it.
204  *
205  * \param mutex Mutex pointer
206  */
207 void thread_mutex_lock_nested(struct thread_mutex *mutex)
208 {
209     dispatcher_handle_t handle = disp_disable();
210     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
211
212     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_NESTED_ENTER,
213                 (uintptr_t)mutex);
214
215     acquire_spinlock(&mutex->lock);
216     if (mutex->locked > 0
217         && mutex->holder != disp_gen->current) {
218         thread_block_and_release_spinlock_disabled(handle, &mutex->queue,
219                                                    &mutex->lock);
220     } else {
221         mutex->locked++;
222         mutex->holder = disp_gen->current;
223         release_spinlock(&mutex->lock);
224         disp_enable(handle);
225     }
226
227     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_NESTED_LEAVE,
228                 (uintptr_t)mutex);
229 }
230
231 /**
232  * \brief Try to lock a mutex
233  *
234  * If the given mutex is unlocked, this atomically locks it and returns true,
235  * otherwise it returns false immediately.
236  *
237  * \param mutex Mutex pointer
238  *
239  * \returns true if lock acquired, false otherwise
240  */
241 bool thread_mutex_trylock(struct thread_mutex *mutex)
242 {
243     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_TRYLOCK,
244                 (uintptr_t)mutex);
245
246     // Try first to avoid contention
247     if (mutex->locked > 0) {
248         return false;
249     }
250
251     dispatcher_handle_t handle = disp_disable();
252     struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
253     bool ret;
254
255     acquire_spinlock(&mutex->lock);
256     if (mutex->locked > 0) {
257         ret = false;
258     } else {
259         ret = true;
260         mutex->locked = 1;
261         mutex->holder = disp_gen->current;
262     }
263     release_spinlock(&mutex->lock);
264
265     disp_enable(handle);
266     return ret;
267 }
268
269 /**
270  * \brief Unlock a mutex, while disabled
271  *
272  * This function unlocks the given mutex. It may only be called while disabled.
273  *
274  * \param disp Dispatcher pointer
275  * \param mutex Mutex pointer
276  *
277  * \return Pointer to thread to be woken on foreign dispatcher
278  */
279 struct thread *thread_mutex_unlock_disabled(dispatcher_handle_t handle,
280                                             struct thread_mutex *mutex)
281 {
282     struct thread *ft = NULL;
283
284     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_UNLOCK,
285                 (uintptr_t)mutex);
286
287     acquire_spinlock(&mutex->lock);
288     assert_disabled(mutex->locked > 0);
289
290     if(mutex->locked == 1) {
291         // Wakeup one waiting thread
292         if (mutex->queue != NULL) {
293             // XXX: This assumes dequeueing is off the top of the queue
294             mutex->holder = mutex->queue;
295             ft = thread_unblock_one_disabled(handle, &mutex->queue, NULL);
296         } else {
297             mutex->holder = NULL;
298             mutex->locked = 0;
299         }
300     } else {
301         mutex->locked--;
302     }
303
304     release_spinlock(&mutex->lock);
305     return ft;
306 }
307
308 /**
309  * \brief Unlock a mutex
310  *
311  * This unlocks the given mutex.
312  *
313  * \param mutex Mutex pointer
314  */
315 void thread_mutex_unlock(struct thread_mutex *mutex)
316 {
317     dispatcher_handle_t disp = disp_disable();
318     struct thread *wakeup = thread_mutex_unlock_disabled(disp, mutex);
319     errval_t err = SYS_ERR_OK;
320
321     if (wakeup != NULL) {
322         err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
323     }
324     disp_enable(disp);
325
326     if(err_is_fail(err)) {
327         USER_PANIC_ERR(err, "remote wakeup from mutex unlock");
328     }
329
330     if(wakeup != NULL) {
331         // XXX: Need directed yield to inter-disp thread
332         thread_yield();
333     }
334 }
335
336 void thread_sem_init(struct thread_sem *sem, unsigned int value)
337 {
338     assert(sem != NULL);
339
340     sem->value = value;
341     sem->queue = NULL;
342     sem->lock = 0;
343 }
344
345 void thread_sem_wait(struct thread_sem *sem)
346 {
347     assert(sem != NULL);
348
349     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_WAIT_ENTER,
350                 (uintptr_t)sem);
351
352     dispatcher_handle_t disp = disp_disable();
353     acquire_spinlock(&sem->lock);
354
355     if(sem->value < 1) {
356         // Not possible to decrement -- wait!
357         thread_block_and_release_spinlock_disabled(disp, &sem->queue, &sem->lock);
358     } else {
359         // Decrement possible
360         sem->value--;
361         release_spinlock(&sem->lock);
362         disp_enable(disp);
363     }
364
365     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_WAIT_LEAVE,
366                 (uintptr_t)sem);
367 }
368
369 bool thread_sem_trywait(struct thread_sem *sem)
370 {
371     assert(sem != NULL);
372     bool ret = false;
373
374     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_TRYWAIT,
375                 (uintptr_t)sem);
376
377     dispatcher_handle_t disp = disp_disable();
378     acquire_spinlock(&sem->lock);
379
380     if(sem->value >= 1) {
381         // Decrement possible
382         sem->value--;
383         ret = true;
384     }
385
386     release_spinlock(&sem->lock);
387     disp_enable(disp);
388
389     return ret;
390 }
391
392 void thread_sem_post(struct thread_sem *sem)
393 {
394     assert(sem != NULL);
395
396     trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_POST, (uintptr_t)sem);
397
398     dispatcher_handle_t disp = disp_disable();
399     struct thread *wakeup = NULL;
400     errval_t err = SYS_ERR_OK;
401     acquire_spinlock(&sem->lock);
402
403     // Wakeup one?
404     if(sem->value == 0 && sem->queue != NULL) {
405         wakeup = thread_unblock_one_disabled(disp, &sem->queue, NULL);
406     } else {
407         sem->value++;
408     }
409
410     if(wakeup != NULL) {
411         err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
412         assert_disabled(err_is_ok(err));
413     }
414
415     release_spinlock(&sem->lock);
416     disp_enable(disp);
417
418     if(err_is_fail(err)) {
419         USER_PANIC_ERR(err, "remote wakeup from semaphore post");
420     }
421
422     if(wakeup != NULL) {
423         // XXX: Need directed yield to inter-disp thread
424         thread_yield();
425     }
426 }