parlib: Add __uth_sync_swap and __uth_sync_is_empty
[akaros.git] / user / parlib / mutex.c
index 314be10..ca7ed3b 100644 (file)
@@ -108,7 +108,8 @@ static void __semaphore_cb(struct uthread *uth, void *arg)
         *
         * Also note the lock-ordering rule.  The sem lock is grabbed before any
         * locks the 2LS might grab. */
-       uthread_has_blocked(uth, &sem->sync_obj, UTH_EXT_BLK_MUTEX);
+       uthread_has_blocked(uth, UTH_EXT_BLK_MUTEX);
+       __uth_sync_enqueue(uth, &sem->sync_obj);
        spin_pdr_unlock(&sem->lock);
 }
 
@@ -118,6 +119,7 @@ bool uth_semaphore_timed_down(uth_semaphore_t *sem,
        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) {
@@ -154,6 +156,7 @@ 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) {
@@ -285,9 +288,8 @@ void uth_recurse_mutex_free(uth_recurse_mutex_t *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);
-       assert(!in_vcore_context());
-       assert(current_uthread);
        /* 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
@@ -316,9 +318,8 @@ 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);
-       assert(!in_vcore_context());
-       assert(current_uthread);
        if (r_mtx->lockholder == current_uthread) {
                r_mtx->count++;
                return TRUE;
@@ -396,7 +397,8 @@ static void __cv_wait_cb(struct uthread *uth, void *arg)
         *
         * Also note the lock-ordering rule.  The cv lock is grabbed before any
         * locks the 2LS might grab. */
-       uthread_has_blocked(uth, &cv->sync_obj, UTH_EXT_BLK_MUTEX);
+       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
@@ -472,6 +474,7 @@ bool uth_cond_var_timed_wait(uth_cond_var_t *cv, uth_mutex_t *mtx,
        struct timeout_blob blob[1];
        bool ret = TRUE;
 
+       assert_can_block();
        parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv);
        link.cv = cv;
        link.mtx = mtx;
@@ -607,12 +610,14 @@ static void __rwlock_rd_cb(struct uthread *uth, void *arg)
 {
        struct uth_rwlock *rwl = (struct uth_rwlock*)arg;
 
-       uthread_has_blocked(uth, &rwl->readers, UTH_EXT_BLK_MUTEX);
+       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 */
@@ -628,6 +633,7 @@ 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) {
@@ -642,12 +648,14 @@ static void __rwlock_wr_cb(struct uthread *uth, void *arg)
 {
        struct uth_rwlock *rwl = (struct uth_rwlock*)arg;
 
-       uthread_has_blocked(uth, &rwl->writers, UTH_EXT_BLK_MUTEX);
+       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 */
@@ -663,6 +671,7 @@ 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) {
@@ -743,6 +752,13 @@ static void uth_default_sync_destroy(uth_sync_t *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;
@@ -768,16 +784,23 @@ static bool uth_default_sync_get_uth(uth_sync_t *sync, struct uthread *uth)
        return FALSE;
 }
 
-/************** External uthread sync interface **************/
+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);
+}
 
-/* Called by the 2LS->has_blocked op, if they are using the default sync.*/
-void __uth_default_sync_enqueue(struct uthread *uth, uth_sync_t *sync)
+static bool uth_default_sync_is_empty(uth_sync_t *sync)
 {
        struct uth_tailq *tq = (struct uth_tailq*)sync;
 
-       TAILQ_INSERT_TAIL(tq, uth, sync_next);
+       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)
 {
@@ -798,6 +821,16 @@ void __uth_sync_destroy(uth_sync_t *sync)
        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)
 {
@@ -814,3 +847,21 @@ bool __uth_sync_get_uth(uth_sync_t *sync, struct uthread *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);
+}