|  | /* Copyright (c) 2016-2017 Google, Inc. | 
|  | * Barret Rhoden <brho@cs.berkeley.edu> | 
|  | * See LICENSE for details. */ | 
|  |  | 
|  | /* Generic Uthread Semaphores, Mutexes, CVs, and other synchronization | 
|  | * functions.  2LSs implement their own sync objects (bottom of the file). */ | 
|  |  | 
|  | #include <parlib/uthread.h> | 
|  | #include <sys/queue.h> | 
|  | #include <parlib/spinlock.h> | 
|  | #include <parlib/alarm.h> | 
|  | #include <parlib/assert.h> | 
|  | #include <malloc.h> | 
|  |  | 
|  | struct timeout_blob { | 
|  | bool				timed_out; | 
|  | struct uthread			*uth; | 
|  | uth_sync_t			*sync_ptr; | 
|  | struct spin_pdr_lock		*lock_ptr; | 
|  | }; | 
|  |  | 
|  | /* When sync primitives want to time out, they can use this alarm handler.  It | 
|  | * needs a timeout_blob, which is independent of any particular sync method. */ | 
|  | static void timeout_handler(struct alarm_waiter *waiter) | 
|  | { | 
|  | struct timeout_blob *blob = (struct timeout_blob*)waiter->data; | 
|  |  | 
|  | spin_pdr_lock(blob->lock_ptr); | 
|  | if (__uth_sync_get_uth(blob->sync_ptr, blob->uth)) | 
|  | blob->timed_out = TRUE; | 
|  | spin_pdr_unlock(blob->lock_ptr); | 
|  | if (blob->timed_out) | 
|  | uthread_runnable(blob->uth); | 
|  | } | 
|  |  | 
|  | /* Minor helper, sets a blob's fields */ | 
|  | static void set_timeout_blob(struct timeout_blob *blob, uth_sync_t *sync_ptr, | 
|  | struct spin_pdr_lock *lock_ptr) | 
|  | { | 
|  | blob->timed_out = FALSE; | 
|  | blob->uth = current_uthread; | 
|  | blob->sync_ptr = sync_ptr; | 
|  | blob->lock_ptr = lock_ptr; | 
|  | } | 
|  |  | 
|  | /* Minor helper, sets an alarm for blob and a timespec */ | 
|  | static void set_timeout_alarm(struct alarm_waiter *waiter, | 
|  | struct timeout_blob *blob, | 
|  | const struct timespec *abs_timeout) | 
|  | { | 
|  | init_awaiter(waiter, timeout_handler); | 
|  | waiter->data = blob; | 
|  | set_awaiter_abs_unix(waiter, timespec_to_alarm_time(abs_timeout)); | 
|  | set_alarm(waiter); | 
|  | } | 
|  |  | 
|  | /************** Semaphores and Mutexes **************/ | 
|  |  | 
|  | static void __uth_semaphore_init(void *arg) | 
|  | { | 
|  | struct uth_semaphore *sem = (struct uth_semaphore*)arg; | 
|  |  | 
|  | spin_pdr_init(&sem->lock); | 
|  | __uth_sync_init(&sem->sync_obj); | 
|  | /* If we used a static initializer for a semaphore, count is already | 
|  | * set.  o/w it will be set by _alloc() or _init() (via | 
|  | * uth_semaphore_init()). */ | 
|  | } | 
|  |  | 
|  | /* Initializes a sem acquired from somewhere else.  POSIX's sem_init() needs | 
|  | * this. */ | 
|  | void uth_semaphore_init(uth_semaphore_t *sem, unsigned int count) | 
|  | { | 
|  | __uth_semaphore_init(sem); | 
|  | sem->count = count; | 
|  | /* The once is to make sure the object is initialized. */ | 
|  | parlib_set_ran_once(&sem->once_ctl); | 
|  | } | 
|  |  | 
|  | /* Undoes whatever was done in init. */ | 
|  | void uth_semaphore_destroy(uth_semaphore_t *sem) | 
|  | { | 
|  | __uth_sync_destroy(&sem->sync_obj); | 
|  | } | 
|  |  | 
|  | uth_semaphore_t *uth_semaphore_alloc(unsigned int count) | 
|  | { | 
|  | struct uth_semaphore *sem; | 
|  |  | 
|  | sem = malloc(sizeof(struct uth_semaphore)); | 
|  | assert(sem); | 
|  | uth_semaphore_init(sem, count); | 
|  | return sem; | 
|  | } | 
|  |  | 
|  | void uth_semaphore_free(uth_semaphore_t *sem) | 
|  | { | 
|  | uth_semaphore_destroy(sem); | 
|  | free(sem); | 
|  | } | 
|  |  | 
|  | static void __semaphore_cb(struct uthread *uth, void *arg) | 
|  | { | 
|  | struct uth_semaphore *sem = (struct uth_semaphore*)arg; | 
|  |  | 
|  | /* We need to tell the 2LS that its thread blocked.  We need to do this | 
|  | * before unlocking the sem, since as soon as we unlock, the sem could | 
|  | * be released and our thread restarted. | 
|  | * | 
|  | * Also note the lock-ordering rule.  The sem lock is grabbed before any | 
|  | * locks the 2LS might grab. */ | 
|  | uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX); | 
|  | __uth_sync_enqueue(uth, &sem->sync_obj); | 
|  | spin_pdr_unlock(&sem->lock); | 
|  | } | 
|  |  | 
|  | bool uth_semaphore_timed_down(uth_semaphore_t *sem, | 
|  | const struct timespec *abs_timeout) | 
|  | { | 
|  | struct alarm_waiter waiter[1]; | 
|  | struct timeout_blob blob[1]; | 
|  |  | 
|  | assert_can_block(); | 
|  | parlib_run_once(&sem->once_ctl, __uth_semaphore_init, sem); | 
|  | spin_pdr_lock(&sem->lock); | 
|  | if (sem->count > 0) { | 
|  | /* Only down if we got one.  This means a sem with no more | 
|  | * counts is 0, not negative (where -count == nr_waiters). | 
|  | * Doing it this way means our timeout function works for sems | 
|  | * and CVs. */ | 
|  | sem->count--; | 
|  | spin_pdr_unlock(&sem->lock); | 
|  | return TRUE; | 
|  | } | 
|  | if (abs_timeout) { | 
|  | set_timeout_blob(blob, &sem->sync_obj, &sem->lock); | 
|  | set_timeout_alarm(waiter, blob, abs_timeout); | 
|  | } | 
|  | /* the unlock and sync enqueuing is done in the yield callback.  as | 
|  | * always, we need to do this part in vcore context, since as soon as we | 
|  | * unlock the uthread could restart.  (atomically yield and unlock). */ | 
|  | uthread_yield(TRUE, __semaphore_cb, sem); | 
|  | if (abs_timeout) { | 
|  | /* We're guaranteed the alarm will either be cancelled or the | 
|  | * handler complete when unset_alarm() returns. */ | 
|  | unset_alarm(waiter); | 
|  | return blob->timed_out ? FALSE : TRUE; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | void uth_semaphore_down(uth_semaphore_t *sem) | 
|  | { | 
|  | uth_semaphore_timed_down(sem, NULL); | 
|  | } | 
|  |  | 
|  | bool uth_semaphore_trydown(uth_semaphore_t *sem) | 
|  | { | 
|  | bool ret = FALSE; | 
|  |  | 
|  | assert_can_block(); | 
|  | parlib_run_once(&sem->once_ctl, __uth_semaphore_init, sem); | 
|  | spin_pdr_lock(&sem->lock); | 
|  | if (sem->count > 0) { | 
|  | sem->count--; | 
|  | ret = TRUE; | 
|  | } | 
|  | spin_pdr_unlock(&sem->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void uth_semaphore_up(uth_semaphore_t *sem) | 
|  | { | 
|  | struct uthread *uth; | 
|  |  | 
|  | /* once-ing the 'up', unlike mtxs 'unlock', since sems can be special. | 
|  | */ | 
|  | parlib_run_once(&sem->once_ctl, __uth_semaphore_init, sem); | 
|  | spin_pdr_lock(&sem->lock); | 
|  | uth = __uth_sync_get_next(&sem->sync_obj); | 
|  | /* If there was a waiter, we pass our resource/count to them. */ | 
|  | if (!uth) | 
|  | sem->count++; | 
|  | spin_pdr_unlock(&sem->lock); | 
|  | if (uth) | 
|  | uthread_runnable(uth); | 
|  | } | 
|  |  | 
|  | /* Takes a void * since it's called by parlib_run_once(), which enables us to | 
|  | * statically initialize the mutex.  This init does everything not done by the | 
|  | * static initializer.  Note we do not allow 'static' destruction.  (No one | 
|  | * calls free). */ | 
|  | static void __uth_mutex_init(void *arg) | 
|  | { | 
|  | struct uth_semaphore *mtx = (struct uth_semaphore*)arg; | 
|  |  | 
|  | __uth_semaphore_init(mtx); | 
|  | mtx->count = 1; | 
|  | } | 
|  |  | 
|  | void uth_mutex_init(uth_mutex_t *mtx) | 
|  | { | 
|  | __uth_mutex_init(mtx); | 
|  | parlib_set_ran_once(&mtx->once_ctl); | 
|  | } | 
|  |  | 
|  | void uth_mutex_destroy(uth_mutex_t *mtx) | 
|  | { | 
|  | uth_semaphore_destroy(mtx); | 
|  | } | 
|  |  | 
|  | uth_mutex_t *uth_mutex_alloc(void) | 
|  | { | 
|  | struct uth_semaphore *mtx; | 
|  |  | 
|  | mtx = malloc(sizeof(struct uth_semaphore)); | 
|  | assert(mtx); | 
|  | uth_mutex_init(mtx); | 
|  | return mtx; | 
|  | } | 
|  |  | 
|  | void uth_mutex_free(uth_mutex_t *mtx) | 
|  | { | 
|  | uth_semaphore_free(mtx); | 
|  | } | 
|  |  | 
|  | bool uth_mutex_timed_lock(uth_mutex_t *mtx, const struct timespec *abs_timeout) | 
|  | { | 
|  | parlib_run_once(&mtx->once_ctl, __uth_mutex_init, mtx); | 
|  | return uth_semaphore_timed_down(mtx, abs_timeout); | 
|  | } | 
|  |  | 
|  | void uth_mutex_lock(uth_mutex_t *mtx) | 
|  | { | 
|  | parlib_run_once(&mtx->once_ctl, __uth_mutex_init, mtx); | 
|  | uth_semaphore_down(mtx); | 
|  | } | 
|  |  | 
|  | bool uth_mutex_trylock(uth_mutex_t *mtx) | 
|  | { | 
|  | parlib_run_once(&mtx->once_ctl, __uth_mutex_init, mtx); | 
|  | return uth_semaphore_trydown(mtx); | 
|  | } | 
|  |  | 
|  | void uth_mutex_unlock(uth_mutex_t *mtx) | 
|  | { | 
|  | uth_semaphore_up(mtx); | 
|  | } | 
|  |  | 
|  | /************** Recursive mutexes **************/ | 
|  |  | 
|  | static void __uth_recurse_mutex_init(void *arg) | 
|  | { | 
|  | struct uth_recurse_mutex *r_mtx = (struct uth_recurse_mutex*)arg; | 
|  |  | 
|  | __uth_mutex_init(&r_mtx->mtx); | 
|  | /* Since we always manually call __uth_mutex_init(), there's no reason | 
|  | * to mess with the regular mutex's static initializer.  Just say it's | 
|  | * been done. */ | 
|  | parlib_set_ran_once(&r_mtx->mtx.once_ctl); | 
|  | r_mtx->lockholder = NULL; | 
|  | r_mtx->count = 0; | 
|  | } | 
|  |  | 
|  | void uth_recurse_mutex_init(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | __uth_recurse_mutex_init(r_mtx); | 
|  | parlib_set_ran_once(&r_mtx->once_ctl); | 
|  | } | 
|  |  | 
|  | void uth_recurse_mutex_destroy(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | uth_semaphore_destroy(&r_mtx->mtx); | 
|  | } | 
|  |  | 
|  | uth_recurse_mutex_t *uth_recurse_mutex_alloc(void) | 
|  | { | 
|  | struct uth_recurse_mutex *r_mtx = | 
|  | malloc(sizeof(struct uth_recurse_mutex)); | 
|  |  | 
|  | assert(r_mtx); | 
|  | uth_recurse_mutex_init(r_mtx); | 
|  | return r_mtx; | 
|  | } | 
|  |  | 
|  | void uth_recurse_mutex_free(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | uth_recurse_mutex_destroy(r_mtx); | 
|  | free(r_mtx); | 
|  | } | 
|  |  | 
|  | bool uth_recurse_mutex_timed_lock(uth_recurse_mutex_t *r_mtx, | 
|  | const struct timespec *abs_timeout) | 
|  | { | 
|  | assert_can_block(); | 
|  | parlib_run_once(&r_mtx->once_ctl, __uth_recurse_mutex_init, r_mtx); | 
|  | /* We don't have to worry about races on current_uthread or count.  They | 
|  | * are only written by the initial lockholder, and this check will only | 
|  | * be true for the initial lockholder, which cannot concurrently call | 
|  | * this function twice (a thread is single-threaded). | 
|  | * | 
|  | * A signal handler running for a thread should not attempt to grab a | 
|  | * recursive mutex (that's probably a bug).  If we need to support that, | 
|  | * we'll have to disable notifs temporarily. */ | 
|  | if (r_mtx->lockholder == current_uthread) { | 
|  | r_mtx->count++; | 
|  | return TRUE; | 
|  | } | 
|  | if (!uth_mutex_timed_lock(&r_mtx->mtx, abs_timeout)) | 
|  | return FALSE; | 
|  | r_mtx->lockholder = current_uthread; | 
|  | r_mtx->count = 1; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | void uth_recurse_mutex_lock(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | uth_recurse_mutex_timed_lock(r_mtx, NULL); | 
|  | } | 
|  |  | 
|  | bool uth_recurse_mutex_trylock(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | bool ret; | 
|  |  | 
|  | assert_can_block(); | 
|  | parlib_run_once(&r_mtx->once_ctl, __uth_recurse_mutex_init, r_mtx); | 
|  | if (r_mtx->lockholder == current_uthread) { | 
|  | r_mtx->count++; | 
|  | return TRUE; | 
|  | } | 
|  | ret = uth_mutex_trylock(&r_mtx->mtx); | 
|  | if (ret) { | 
|  | r_mtx->lockholder = current_uthread; | 
|  | r_mtx->count = 1; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void uth_recurse_mutex_unlock(uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | r_mtx->count--; | 
|  | if (!r_mtx->count) { | 
|  | r_mtx->lockholder = NULL; | 
|  | uth_mutex_unlock(&r_mtx->mtx); | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | /************** Condition Variables **************/ | 
|  |  | 
|  |  | 
|  | static void __uth_cond_var_init(void *arg) | 
|  | { | 
|  | struct uth_cond_var *cv = (struct uth_cond_var*)arg; | 
|  |  | 
|  | spin_pdr_init(&cv->lock); | 
|  | __uth_sync_init(&cv->sync_obj); | 
|  | } | 
|  |  | 
|  | void uth_cond_var_init(uth_cond_var_t *cv) | 
|  | { | 
|  | __uth_cond_var_init(cv); | 
|  | parlib_set_ran_once(&cv->once_ctl); | 
|  | } | 
|  |  | 
|  | void uth_cond_var_destroy(uth_cond_var_t *cv) | 
|  | { | 
|  | __uth_sync_destroy(&cv->sync_obj); | 
|  | } | 
|  |  | 
|  | uth_cond_var_t *uth_cond_var_alloc(void) | 
|  | { | 
|  | struct uth_cond_var *cv; | 
|  |  | 
|  | cv = malloc(sizeof(struct uth_cond_var)); | 
|  | assert(cv); | 
|  | uth_cond_var_init(cv); | 
|  | return cv; | 
|  | } | 
|  |  | 
|  | void uth_cond_var_free(uth_cond_var_t *cv) | 
|  | { | 
|  | uth_cond_var_destroy(cv); | 
|  | free(cv); | 
|  | } | 
|  |  | 
|  | struct uth_cv_link { | 
|  | struct uth_cond_var			*cv; | 
|  | struct uth_semaphore		*mtx; | 
|  | }; | 
|  |  | 
|  | static void __cv_wait_cb(struct uthread *uth, void *arg) | 
|  | { | 
|  | struct uth_cv_link *link = (struct uth_cv_link*)arg; | 
|  | struct uth_cond_var *cv = link->cv; | 
|  | struct uth_semaphore *mtx = link->mtx; | 
|  |  | 
|  | /* We need to tell the 2LS that its thread blocked.  We need to do this | 
|  | * before unlocking the cv, since as soon as we unlock, the cv could be | 
|  | * signalled and our thread restarted. | 
|  | * | 
|  | * Also note the lock-ordering rule.  The cv lock is grabbed before any | 
|  | * locks the 2LS might grab. */ | 
|  | uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX); | 
|  | __uth_sync_enqueue(uth, &cv->sync_obj); | 
|  | spin_pdr_unlock(&cv->lock); | 
|  | /* This looks dangerous, since both the CV and MTX could use the | 
|  | * uth->sync_next TAILQ_ENTRY (or whatever the 2LS uses), but the | 
|  | * uthread never sleeps on both at the same time.  We *hold* the mtx - | 
|  | * we aren't *sleeping* on it.  Sleeping uses the sync_next.  Holding it | 
|  | * doesn't. | 
|  | * | 
|  | * Next, consider what happens as soon as we unlock the CV.  Our thread | 
|  | * could get woken up, and then immediately try to grab the mtx and go | 
|  | * to sleep! (see below).  If that happens, the uthread is no longer | 
|  | * sleeping on the CV, and the sync_next is free.  The invariant is that | 
|  | * a uthread can only sleep on one sync_object at a time. */ | 
|  | if (mtx) | 
|  | uth_mutex_unlock(mtx); | 
|  | } | 
|  |  | 
|  | /* Caller holds mtx.  We will 'atomically' release it and wait.  On return, | 
|  | * caller holds mtx again.  Once our uth is on the CV's list, we can release the | 
|  | * mtx without fear of missing a signal. | 
|  | * | 
|  | * POSIX refers to atomicity in this context as "atomically with respect to | 
|  | * access by another thread to the mutex and then the condition variable" | 
|  | * | 
|  | * The idea is that we hold the mutex to protect some invariant; we check it, | 
|  | * and decide to sleep.  Now we get on the list before releasing so that any | 
|  | * changes to that invariant (e.g. a flag is now TRUE) happen after we're on the | 
|  | * list, and so that we don't miss the signal.  To be more clear, the invariant | 
|  | * in a basic wake-up flag scenario is: "whenever a flag is set from FALSE to | 
|  | * TRUE, all waiters that saw FALSE are on the CV's waitqueue."  The mutex is | 
|  | * required for this invariant. | 
|  | * | 
|  | * Note that signal/broadcasters do not *need* to hold the mutex, in general, | 
|  | * but they do in the basic wake-up flag scenario.  If not, the race is this: | 
|  | * | 
|  | * Sleeper:					Waker: | 
|  | * ----------------------------------------------------------------- | 
|  | * Hold mutex | 
|  | *   See flag is False | 
|  | *   Decide to sleep | 
|  | *						Set flag True | 
|  | * PAUSE!					Grab CV lock | 
|  | *						See list is empty, unlock | 
|  | * | 
|  | *   Grab CV lock | 
|  | *     Get put on list | 
|  | *   Unlock CV lock | 
|  | * Unlock mutex | 
|  | * (Never wake up; we missed the signal) | 
|  | * | 
|  | * For those familiar with the kernel's CVs, we don't couple mutexes with CVs. | 
|  | * cv_lock() actually grabs the spinlock inside the CV and uses *that* to | 
|  | * protect the invariant.  The signallers always grab that lock, so the sleeper | 
|  | * is not in danger of missing the signal.  The tradeoff is that the kernel CVs | 
|  | * use a spinlock instead of a mutex for protecting its invariant; there might | 
|  | * be some case that preferred blocking sync. | 
|  | * | 
|  | * The uthread CVs take a mutex, unlike the kernel CVs, to map more cleanly to | 
|  | * POSIX CVs.  Maybe one approach or the other is a bad idea; we'll see. | 
|  | * However, we need both approaces in userspace.  To that end, we also support | 
|  | * mutex-less CVs, where the synchronization typically provided by the mutex is | 
|  | * provided by the CV's spinlock.  Just pass NULL for the mutex.  This is | 
|  | * primarily useful for CVs that are signalled from event handlers in vcore | 
|  | * context, since that code cannot block on a mutex and thus cannot use the | 
|  | * mutex to avoid the races mentioned above. | 
|  | * | 
|  | * As far as lock ordering goes, once the sleeper holds the mutex and is on the | 
|  | * CV's list, it can unlock in any order it wants.  However, unlocking a mutex | 
|  | * actually requires grabbing its spinlock.  So as to not have a lock ordering | 
|  | * between *spinlocks*, we let go of the CV's spinlock before unlocking the | 
|  | * mutex.  There is an ordering between the mutex and the CV spinlock (mutex->cv | 
|  | * spin), but there is no ordering between the mutex spin and cv spin.  And of | 
|  | * course, we need to unlock the CV spinlock in the yield callback. | 
|  | * | 
|  | * Also note that we use the external API for the mutex operations.  A 2LS could | 
|  | * have their own mutex ops but still use the generic cv ops. */ | 
|  | bool uth_cond_var_timed_wait(uth_cond_var_t *cv, uth_mutex_t *mtx, | 
|  | const struct timespec *abs_timeout) | 
|  | { | 
|  | struct uth_cv_link link; | 
|  | struct alarm_waiter waiter[1]; | 
|  | struct timeout_blob blob[1]; | 
|  | bool ret = TRUE; | 
|  |  | 
|  | /* We're holding the CV PDR lock, so we lose the ability to detect | 
|  | * blocking violations. */ | 
|  | if (mtx) | 
|  | assert_can_block(); | 
|  | parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv); | 
|  | link.cv = cv; | 
|  | link.mtx = mtx; | 
|  | if (mtx) | 
|  | spin_pdr_lock(&cv->lock); | 
|  | if (abs_timeout) { | 
|  | set_timeout_blob(blob, &cv->sync_obj, &cv->lock); | 
|  | set_timeout_alarm(waiter, blob, abs_timeout); | 
|  | } | 
|  | uthread_yield(TRUE, __cv_wait_cb, &link); | 
|  | if (abs_timeout) { | 
|  | unset_alarm(waiter); | 
|  | ret = blob->timed_out ? FALSE : TRUE; | 
|  | } | 
|  | if (mtx) | 
|  | uth_mutex_lock(mtx); | 
|  | else | 
|  | spin_pdr_lock(&cv->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | void uth_cond_var_wait(uth_cond_var_t *cv, uth_mutex_t *mtx) | 
|  | { | 
|  | uth_cond_var_timed_wait(cv, mtx, NULL); | 
|  | } | 
|  |  | 
|  | /* GCC doesn't list this as one of the C++0x functions, but it's easy to do and | 
|  | * implement uth_cond_var_wait_recurse() with it, just like for all the other | 
|  | * 'timed' functions. | 
|  | * | 
|  | * Note the timeout applies to getting the signal on the CV, not on reacquiring | 
|  | * the mutex. */ | 
|  | bool uth_cond_var_timed_wait_recurse(uth_cond_var_t *cv, | 
|  | uth_recurse_mutex_t *r_mtx, | 
|  | const struct timespec *abs_timeout) | 
|  | { | 
|  | unsigned int old_count = r_mtx->count; | 
|  | bool ret; | 
|  |  | 
|  | /* In cond_wait, we're going to unlock the internal mutex.  We'll do the | 
|  | * prep-work for that now.  (invariant is that an unlocked r_mtx has no | 
|  | * lockholder and count == 0. */ | 
|  | r_mtx->lockholder = NULL; | 
|  | r_mtx->count = 0; | 
|  | ret = uth_cond_var_timed_wait(cv, &r_mtx->mtx, abs_timeout); | 
|  | /* Now we hold the internal mutex again.  Need to restore the tracking. | 
|  | */ | 
|  | r_mtx->lockholder = current_uthread; | 
|  | r_mtx->count = old_count; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* GCC wants this function, though its semantics are a little unclear.  I | 
|  | * imagine you'd want to completely unlock it (say you locked it 3 times), and | 
|  | * when you get it back, that you have your three locks back. */ | 
|  | void uth_cond_var_wait_recurse(uth_cond_var_t *cv, uth_recurse_mutex_t *r_mtx) | 
|  | { | 
|  | uth_cond_var_timed_wait_recurse(cv, r_mtx, NULL); | 
|  | } | 
|  |  | 
|  | /* Caller holds the CV lock.  Returns a uth that needs to be woken up (or NULL), | 
|  | * which the caller needs to do with uthread_runnable(). */ | 
|  | struct uthread *__uth_cond_var_wake_one(uth_cond_var_t *cv) | 
|  | { | 
|  | return __uth_sync_get_next(&cv->sync_obj); | 
|  | } | 
|  |  | 
|  | /* Caller holds the CV lock. */ | 
|  | void __uth_cond_var_signal_and_unlock(uth_cond_var_t *cv) | 
|  | { | 
|  | struct uthread *uth = __uth_cond_var_wake_one(cv); | 
|  |  | 
|  | spin_pdr_unlock(&cv->lock); | 
|  | if (uth) | 
|  | uthread_runnable(uth); | 
|  | } | 
|  |  | 
|  | void uth_cond_var_signal(uth_cond_var_t *cv) | 
|  | { | 
|  | parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv); | 
|  |  | 
|  | spin_pdr_lock(&cv->lock); | 
|  | __uth_cond_var_signal_and_unlock(cv); | 
|  | } | 
|  |  | 
|  | /* Caller holds the CV lock.  Returns true if the restartees need to be woken | 
|  | * up, which the caller needs to do with __uth_sync_wake_all(). */ | 
|  | bool __uth_cond_var_wake_all(uth_cond_var_t *cv, uth_sync_t *restartees) | 
|  | { | 
|  | if (__uth_sync_is_empty(&cv->sync_obj)) | 
|  | return false; | 
|  | __uth_sync_init(restartees); | 
|  | __uth_sync_swap(restartees, &cv->sync_obj); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* Caller holds the CV lock. */ | 
|  | void __uth_cond_var_broadcast_and_unlock(uth_cond_var_t *cv) | 
|  | { | 
|  | uth_sync_t restartees; | 
|  | bool wake; | 
|  |  | 
|  | wake = __uth_cond_var_wake_all(cv, &restartees); | 
|  | spin_pdr_unlock(&cv->lock); | 
|  | if (wake) | 
|  | __uth_sync_wake_all(&restartees); | 
|  | } | 
|  |  | 
|  | void uth_cond_var_broadcast(uth_cond_var_t *cv) | 
|  | { | 
|  | parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv); | 
|  |  | 
|  | spin_pdr_lock(&cv->lock); | 
|  | __uth_cond_var_broadcast_and_unlock(cv); | 
|  | } | 
|  |  | 
|  | /* Similar to the kernel, we can grab the CV's spinlock directly and use that | 
|  | * for synchronization.  This is primarily so we can signal/broadcast from vcore | 
|  | * context, and you typically need to hold some lock when changing state before | 
|  | * signalling. */ | 
|  | void uth_cond_var_lock(uth_cond_var_t *cv) | 
|  | { | 
|  | parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv); | 
|  |  | 
|  | spin_pdr_lock(&cv->lock); | 
|  | } | 
|  |  | 
|  | void uth_cond_var_unlock(uth_cond_var_t *cv) | 
|  | { | 
|  | spin_pdr_unlock(&cv->lock); | 
|  | } | 
|  |  | 
|  | /************** Reader-writer Sleeping Locks **************/ | 
|  |  | 
|  |  | 
|  | static void __uth_rwlock_init(void *arg) | 
|  | { | 
|  | struct uth_rwlock *rwl = (struct uth_rwlock*)arg; | 
|  |  | 
|  | spin_pdr_init(&rwl->lock); | 
|  | rwl->nr_readers = 0; | 
|  | rwl->has_writer = FALSE; | 
|  | __uth_sync_init(&rwl->readers); | 
|  | __uth_sync_init(&rwl->writers); | 
|  | } | 
|  |  | 
|  | void uth_rwlock_init(uth_rwlock_t *rwl) | 
|  | { | 
|  | __uth_rwlock_init(rwl); | 
|  | parlib_set_ran_once(&rwl->once_ctl); | 
|  | } | 
|  |  | 
|  | void uth_rwlock_destroy(uth_rwlock_t *rwl) | 
|  | { | 
|  | __uth_sync_destroy(&rwl->readers); | 
|  | __uth_sync_destroy(&rwl->writers); | 
|  | } | 
|  |  | 
|  | uth_rwlock_t *uth_rwlock_alloc(void) | 
|  | { | 
|  | struct uth_rwlock *rwl; | 
|  |  | 
|  | rwl = malloc(sizeof(struct uth_rwlock)); | 
|  | assert(rwl); | 
|  | uth_rwlock_init(rwl); | 
|  | return rwl; | 
|  | } | 
|  |  | 
|  | void uth_rwlock_free(uth_rwlock_t *rwl) | 
|  | { | 
|  | uth_rwlock_destroy(rwl); | 
|  | free(rwl); | 
|  | } | 
|  |  | 
|  | /* Readers and writers block until they have the lock.  The delicacies are dealt | 
|  | * with by the unlocker. */ | 
|  | static void __rwlock_rd_cb(struct uthread *uth, void *arg) | 
|  | { | 
|  | struct uth_rwlock *rwl = (struct uth_rwlock*)arg; | 
|  |  | 
|  | uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX); | 
|  | __uth_sync_enqueue(uth, &rwl->readers); | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | } | 
|  |  | 
|  | void uth_rwlock_rdlock(uth_rwlock_t *rwl) | 
|  | { | 
|  | assert_can_block(); | 
|  | parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl); | 
|  | spin_pdr_lock(&rwl->lock); | 
|  | /* Readers always make progress when there is no writer */ | 
|  | if (!rwl->has_writer) { | 
|  | rwl->nr_readers++; | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | return; | 
|  | } | 
|  | uthread_yield(TRUE, __rwlock_rd_cb, rwl); | 
|  | } | 
|  |  | 
|  | bool uth_rwlock_try_rdlock(uth_rwlock_t *rwl) | 
|  | { | 
|  | bool ret = FALSE; | 
|  |  | 
|  | assert_can_block(); | 
|  | parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl); | 
|  | spin_pdr_lock(&rwl->lock); | 
|  | if (!rwl->has_writer) { | 
|  | rwl->nr_readers++; | 
|  | ret = TRUE; | 
|  | } | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void __rwlock_wr_cb(struct uthread *uth, void *arg) | 
|  | { | 
|  | struct uth_rwlock *rwl = (struct uth_rwlock*)arg; | 
|  |  | 
|  | uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX); | 
|  | __uth_sync_enqueue(uth, &rwl->writers); | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | } | 
|  |  | 
|  | void uth_rwlock_wrlock(uth_rwlock_t *rwl) | 
|  | { | 
|  | assert_can_block(); | 
|  | parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl); | 
|  | spin_pdr_lock(&rwl->lock); | 
|  | /* Writers require total mutual exclusion - no writers or readers */ | 
|  | if (!rwl->has_writer && !rwl->nr_readers) { | 
|  | rwl->has_writer = TRUE; | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | return; | 
|  | } | 
|  | uthread_yield(TRUE, __rwlock_wr_cb, rwl); | 
|  | } | 
|  |  | 
|  | bool uth_rwlock_try_wrlock(uth_rwlock_t *rwl) | 
|  | { | 
|  | bool ret = FALSE; | 
|  |  | 
|  | assert_can_block(); | 
|  | parlib_run_once(&rwl->once_ctl, __uth_rwlock_init, rwl); | 
|  | spin_pdr_lock(&rwl->lock); | 
|  | if (!rwl->has_writer && !rwl->nr_readers) { | 
|  | rwl->has_writer = TRUE; | 
|  | ret = TRUE; | 
|  | } | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Let's try to wake writers (yes, this is a policy decision), and if none, wake | 
|  | * all the readers.  The invariant there is that if there is no writer, then | 
|  | * there are no waiting readers. */ | 
|  | static void __rw_unlock_writer(struct uth_rwlock *rwl, | 
|  | struct uth_tailq *restartees) | 
|  | { | 
|  | struct uthread *uth; | 
|  |  | 
|  | uth = __uth_sync_get_next(&rwl->writers); | 
|  | if (uth) { | 
|  | TAILQ_INSERT_TAIL(restartees, uth, sync_next); | 
|  | } else { | 
|  | rwl->has_writer = FALSE; | 
|  | while ((uth = __uth_sync_get_next(&rwl->readers))) { | 
|  | TAILQ_INSERT_TAIL(restartees, uth, sync_next); | 
|  | rwl->nr_readers++; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void __rw_unlock_reader(struct uth_rwlock *rwl, | 
|  | struct uth_tailq *restartees) | 
|  | { | 
|  | struct uthread *uth; | 
|  |  | 
|  | rwl->nr_readers--; | 
|  | if (!rwl->nr_readers) { | 
|  | uth = __uth_sync_get_next(&rwl->writers); | 
|  | if (uth) { | 
|  | TAILQ_INSERT_TAIL(restartees, uth, sync_next); | 
|  | rwl->has_writer = TRUE; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* Unlock works for either readers or writer locks.  You can tell which you were | 
|  | * based on whether has_writer is set or not. */ | 
|  | void uth_rwlock_unlock(uth_rwlock_t *rwl) | 
|  | { | 
|  | struct uth_tailq restartees = TAILQ_HEAD_INITIALIZER(restartees); | 
|  | struct uthread *i, *safe; | 
|  |  | 
|  | spin_pdr_lock(&rwl->lock); | 
|  | if (rwl->has_writer) | 
|  | __rw_unlock_writer(rwl, &restartees); | 
|  | else | 
|  | __rw_unlock_reader(rwl, &restartees); | 
|  | spin_pdr_unlock(&rwl->lock); | 
|  | TAILQ_FOREACH_SAFE(i, &restartees, sync_next, safe) | 
|  | uthread_runnable(i); | 
|  | } | 
|  |  | 
|  |  | 
|  | /************** Default Sync Obj Implementation **************/ | 
|  |  | 
|  | static void uth_default_sync_init(uth_sync_t *sync) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  |  | 
|  | parlib_static_assert(sizeof(struct uth_tailq) <= sizeof(uth_sync_t)); | 
|  | TAILQ_INIT(tq); | 
|  | } | 
|  |  | 
|  | static void uth_default_sync_destroy(uth_sync_t *sync) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  |  | 
|  | assert(TAILQ_EMPTY(tq)); | 
|  | } | 
|  |  | 
|  | static void uth_default_sync_enqueue(struct uthread *uth, uth_sync_t *sync) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  |  | 
|  | TAILQ_INSERT_TAIL(tq, uth, sync_next); | 
|  | } | 
|  |  | 
|  | static struct uthread *uth_default_sync_get_next(uth_sync_t *sync) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  | struct uthread *first; | 
|  |  | 
|  | first = TAILQ_FIRST(tq); | 
|  | if (first) | 
|  | TAILQ_REMOVE(tq, first, sync_next); | 
|  | return first; | 
|  | } | 
|  |  | 
|  | static bool uth_default_sync_get_uth(uth_sync_t *sync, struct uthread *uth) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  | struct uthread *i; | 
|  |  | 
|  | TAILQ_FOREACH(i, tq, sync_next) { | 
|  | if (i == uth) { | 
|  | TAILQ_REMOVE(tq, i, sync_next); | 
|  | return TRUE; | 
|  | } | 
|  | } | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | static void uth_default_sync_swap(uth_sync_t *a, uth_sync_t *b) | 
|  | { | 
|  | struct uth_tailq *tq_a = (struct uth_tailq*)a; | 
|  | struct uth_tailq *tq_b = (struct uth_tailq*)b; | 
|  |  | 
|  | TAILQ_SWAP(tq_a, tq_b, uthread, sync_next); | 
|  | } | 
|  |  | 
|  | static bool uth_default_sync_is_empty(uth_sync_t *sync) | 
|  | { | 
|  | struct uth_tailq *tq = (struct uth_tailq*)sync; | 
|  |  | 
|  | return TAILQ_EMPTY(tq); | 
|  | } | 
|  |  | 
|  | /************** External uthread sync interface **************/ | 
|  |  | 
|  | /* Called by 2LS-independent sync code when a sync object needs initialized. */ | 
|  | void __uth_sync_init(uth_sync_t *sync) | 
|  | { | 
|  | if (sched_ops->sync_init) { | 
|  | sched_ops->sync_init(sync); | 
|  | return; | 
|  | } | 
|  | uth_default_sync_init(sync); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code when a sync object is destroyed. */ | 
|  | void __uth_sync_destroy(uth_sync_t *sync) | 
|  | { | 
|  | if (sched_ops->sync_destroy) { | 
|  | sched_ops->sync_destroy(sync); | 
|  | return; | 
|  | } | 
|  | uth_default_sync_destroy(sync); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code when a thread blocks on sync */ | 
|  | void __uth_sync_enqueue(struct uthread *uth, uth_sync_t *sync) | 
|  | { | 
|  | if (sched_ops->sync_enqueue) { | 
|  | sched_ops->sync_enqueue(uth, sync); | 
|  | return; | 
|  | } | 
|  | uth_default_sync_enqueue(uth, sync); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code when a thread needs to be woken. */ | 
|  | struct uthread *__uth_sync_get_next(uth_sync_t *sync) | 
|  | { | 
|  | if (sched_ops->sync_get_next) | 
|  | return sched_ops->sync_get_next(sync); | 
|  | return uth_default_sync_get_next(sync); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code when a specific thread needs to be woken. | 
|  | * Returns TRUE if the uthread was blocked on the object, FALSE o/w. */ | 
|  | bool __uth_sync_get_uth(uth_sync_t *sync, struct uthread *uth) | 
|  | { | 
|  | if (sched_ops->sync_get_uth) | 
|  | return sched_ops->sync_get_uth(sync, uth); | 
|  | return uth_default_sync_get_uth(sync, uth); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code to swap members of sync objects. */ | 
|  | void __uth_sync_swap(uth_sync_t *a, uth_sync_t *b) | 
|  | { | 
|  | if (sched_ops->sync_swap) { | 
|  | sched_ops->sync_swap(a, b); | 
|  | return; | 
|  | } | 
|  | uth_default_sync_swap(a, b); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code */ | 
|  | bool __uth_sync_is_empty(uth_sync_t *sync) | 
|  | { | 
|  | if (sched_ops->sync_is_empty) | 
|  | return sched_ops->sync_is_empty(sync); | 
|  | return uth_default_sync_is_empty(sync); | 
|  | } | 
|  |  | 
|  | /* Called by 2LS-independent sync code to wake up all uths on sync.  You should | 
|  | * probably not hold locks while you do this - swap the items to a local sync | 
|  | * object first. */ | 
|  | void __uth_sync_wake_all(uth_sync_t *wakees) | 
|  | { | 
|  | struct uthread *uth_i; | 
|  |  | 
|  | if (sched_ops->thread_bulk_runnable) { | 
|  | sched_ops->thread_bulk_runnable(wakees); | 
|  | } else { | 
|  | while ((uth_i = __uth_sync_get_next(wakees))) | 
|  | uthread_runnable(uth_i); | 
|  | } | 
|  | } |