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