3 * \brief Thread synchronisation primitives.
7 * Copyright (c) 2007, 2008, 2009, 2010, 2012, ETH Zurich.
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.
15 #include <barrelfish/barrelfish.h>
16 #include <barrelfish/dispatch.h>
17 #include <barrelfish/dispatcher_arch.h>
18 #include <trace/trace.h>
19 #include "threads_priv.h"
22 #define trace_event(a,b,c) ((void)0)
27 * \brief Initialise a condition variable
29 * \param cond Condition variable pointer
31 void thread_cond_init(struct thread_cond *cond)
38 * \brief Wait for a condition variable
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
45 * \param cond Condition variable pointer
46 * \param mutex Optional pointer to mutex to unlock.
48 void thread_cond_wait(struct thread_cond *cond, struct thread_mutex *mutex)
50 dispatcher_handle_t disp = disp_disable();
52 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_WAIT_ENTER,
55 acquire_spinlock(&cond->lock);
59 struct thread *wakeup = thread_mutex_unlock_disabled(disp, mutex);
62 errval_t err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
63 assert_disabled(err_is_ok(err));
67 // Block on the condition variable and release spinlock
68 thread_block_and_release_spinlock_disabled(disp, &cond->queue, &cond->lock);
70 // Re-acquire the mutex
72 thread_mutex_lock(mutex);
75 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_WAIT_LEAVE,
80 * \brief Signal a condition variable
82 * This function signals the condition variable, and wakes up one
83 * thread waiting on it.
85 * \param cond Condition variable pointer
87 void thread_cond_signal(struct thread_cond *cond)
89 struct thread *wakeup = NULL;
90 errval_t err = SYS_ERR_OK;
92 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_SIGNAL,
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);
101 err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
104 release_spinlock(&cond->lock);
107 if(err_is_fail(err)) {
108 USER_PANIC_ERR(err, "remote wakeup from condition signal");
112 // XXX: Need directed yield to inter-disp thread
118 * \brief Broadcast signal a condition variable
120 * This function signals the condition variable, and wakes up all
121 * threads waiting on it.
123 * \param cond Condition variable pointer
125 void thread_cond_broadcast(struct thread_cond *cond)
127 struct thread *wakeupq = NULL;
128 bool foreignwakeup = false;
130 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_COND_BROADCAST,
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);
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");
152 // XXX: Need directed yield to inter-disp thread
158 * \brief Initialise a mutex
160 * \param mutex Mutex pointer
162 void thread_mutex_init(struct thread_mutex *mutex)
165 mutex->holder = NULL;
171 * \brief Lock a mutex
173 * This blocks until the given mutex is unlocked, and then atomically locks it.
175 * \param mutex Mutex pointer
177 void thread_mutex_lock(struct thread_mutex *mutex)
179 dispatcher_handle_t handle = disp_disable();
180 struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
182 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_ENTER,
185 acquire_spinlock(&mutex->lock);
186 if (mutex->locked > 0) {
187 thread_block_and_release_spinlock_disabled(handle, &mutex->queue,
191 mutex->holder = disp_gen->current;
192 release_spinlock(&mutex->lock);
196 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_LEAVE,
201 * \brief Lock a mutex
203 * This blocks until the given mutex is unlocked, and then atomically locks it.
205 * \param mutex Mutex pointer
207 void thread_mutex_lock_nested(struct thread_mutex *mutex)
209 dispatcher_handle_t handle = disp_disable();
210 struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
212 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_NESTED_ENTER,
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,
222 mutex->holder = disp_gen->current;
223 release_spinlock(&mutex->lock);
227 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_LOCK_NESTED_LEAVE,
232 * \brief Try to lock a mutex
234 * If the given mutex is unlocked, this atomically locks it and returns true,
235 * otherwise it returns false immediately.
237 * \param mutex Mutex pointer
239 * \returns true if lock acquired, false otherwise
241 bool thread_mutex_trylock(struct thread_mutex *mutex)
243 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_TRYLOCK,
246 // Try first to avoid contention
247 if (mutex->locked > 0) {
251 dispatcher_handle_t handle = disp_disable();
252 struct dispatcher_generic *disp_gen = get_dispatcher_generic(handle);
255 acquire_spinlock(&mutex->lock);
256 if (mutex->locked > 0) {
261 mutex->holder = disp_gen->current;
263 release_spinlock(&mutex->lock);
270 * \brief Unlock a mutex, while disabled
272 * This function unlocks the given mutex. It may only be called while disabled.
274 * \param disp Dispatcher pointer
275 * \param mutex Mutex pointer
277 * \return Pointer to thread to be woken on foreign dispatcher
279 struct thread *thread_mutex_unlock_disabled(dispatcher_handle_t handle,
280 struct thread_mutex *mutex)
282 struct thread *ft = NULL;
284 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_MUTEX_UNLOCK,
287 acquire_spinlock(&mutex->lock);
288 assert_disabled(mutex->locked > 0);
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);
297 mutex->holder = NULL;
304 release_spinlock(&mutex->lock);
309 * \brief Unlock a mutex
311 * This unlocks the given mutex.
313 * \param mutex Mutex pointer
315 void thread_mutex_unlock(struct thread_mutex *mutex)
317 dispatcher_handle_t disp = disp_disable();
318 struct thread *wakeup = thread_mutex_unlock_disabled(disp, mutex);
319 errval_t err = SYS_ERR_OK;
321 if (wakeup != NULL) {
322 err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
326 if(err_is_fail(err)) {
327 USER_PANIC_ERR(err, "remote wakeup from mutex unlock");
331 // XXX: Need directed yield to inter-disp thread
336 void thread_sem_init(struct thread_sem *sem, unsigned int value)
345 void thread_sem_wait(struct thread_sem *sem)
349 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_WAIT_ENTER,
352 dispatcher_handle_t disp = disp_disable();
353 acquire_spinlock(&sem->lock);
356 // Not possible to decrement -- wait!
357 thread_block_and_release_spinlock_disabled(disp, &sem->queue, &sem->lock);
359 // Decrement possible
361 release_spinlock(&sem->lock);
365 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_WAIT_LEAVE,
369 bool thread_sem_trywait(struct thread_sem *sem)
374 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_TRYWAIT,
377 dispatcher_handle_t disp = disp_disable();
378 acquire_spinlock(&sem->lock);
380 if(sem->value >= 1) {
381 // Decrement possible
386 release_spinlock(&sem->lock);
392 void thread_sem_post(struct thread_sem *sem)
396 trace_event(TRACE_SUBSYS_THREADS, TRACE_EVENT_SEM_POST, (uintptr_t)sem);
398 dispatcher_handle_t disp = disp_disable();
399 struct thread *wakeup = NULL;
400 errval_t err = SYS_ERR_OK;
401 acquire_spinlock(&sem->lock);
404 if(sem->value == 0 && sem->queue != NULL) {
405 wakeup = thread_unblock_one_disabled(disp, &sem->queue, NULL);
411 err = domain_wakeup_on_disabled(wakeup->disp, wakeup, disp);
412 assert_disabled(err_is_ok(err));
415 release_spinlock(&sem->lock);
418 if(err_is_fail(err)) {
419 USER_PANIC_ERR(err, "remote wakeup from semaphore post");
423 // XXX: Need directed yield to inter-disp thread