parlib: Add a thread_bulk_runnable() 2LS op
authorBarret Rhoden <brho@cs.berkeley.edu>
Fri, 28 Apr 2017 16:31:29 +0000 (12:31 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:16:41 +0000 (12:16 -0400)
If the 2LS knows it is waking up N threads, it can make better decisions or
otherwise perform better.  This gives us a 3x improvement on
pthread barriers.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
user/parlib/include/parlib/uthread.h
user/parlib/mutex.c
user/pthread/pthread.c

index 6b54c4a..3f2e42c 100644 (file)
@@ -97,6 +97,7 @@ struct uthread *__uth_sync_get_next(uth_sync_t *sync);
 bool __uth_sync_get_uth(uth_sync_t *sync, struct uthread *uth);
 void __uth_sync_swap(uth_sync_t *a, uth_sync_t *b);
 bool __uth_sync_is_empty(uth_sync_t *sync);
+void __uth_sync_wake_all(uth_sync_t *wakees);
 
 /* 2L-Scheduler operations.  Examples in pthread.c. */
 struct schedule_ops {
@@ -119,6 +120,7 @@ struct schedule_ops {
        void (*sync_swap)(uth_sync_t *, uth_sync_t *);
        bool (*sync_is_empty)(uth_sync_t *);
        void (*preempt_pending)(void);
+       void (*thread_bulk_runnable)(uth_sync_t *);
 };
 extern struct schedule_ops *sched_ops;
 
index ca7ed3b..c391c03 100644 (file)
@@ -544,21 +544,18 @@ void uth_cond_var_signal(uth_cond_var_t *cv)
 
 void uth_cond_var_broadcast(uth_cond_var_t *cv)
 {
-       struct uth_tailq restartees = TAILQ_HEAD_INITIALIZER(restartees);
-       struct uthread *i, *safe;
+       uth_sync_t restartees;
 
        parlib_run_once(&cv->once_ctl, __uth_cond_var_init, cv);
        spin_pdr_lock(&cv->lock);
-       /* If this turns out to be slow or painful for 2LSs, we can implement a
-        * get_all or something (default used to use TAILQ_SWAP). */
-       while ((i = __uth_sync_get_next(&cv->sync_obj))) {
-               /* Once the uth is out of the sync obj, we can reuse sync_next. */
-               TAILQ_INSERT_TAIL(&restartees, i, sync_next);
+       if (__uth_sync_is_empty(&cv->sync_obj)) {
+               spin_pdr_unlock(&cv->lock);
+               return;
        }
+       __uth_sync_init(&restartees);
+       __uth_sync_swap(&restartees, &cv->sync_obj);
        spin_pdr_unlock(&cv->lock);
-       /* Need the SAFE, since we can't touch the linkage once the uth could run */
-       TAILQ_FOREACH_SAFE(i, &restartees, sync_next, safe)
-               uthread_runnable(i);
+       __uth_sync_wake_all(&restartees);
 }
 
 
@@ -865,3 +862,18 @@ bool __uth_sync_is_empty(uth_sync_t *sync)
                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);
+       }
+}
index c992b40..00adb42 100644 (file)
@@ -47,6 +47,7 @@ static void pth_thread_refl_fault(struct uthread *uth,
                                   struct user_context *ctx);
 static void pth_thread_exited(struct uthread *uth);
 static struct uthread *pth_thread_create(void *(*func)(void *), void *arg);
+static void pth_thread_bulk_runnable(uth_sync_t *wakees);
 
 /* Event Handlers */
 static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
@@ -62,6 +63,7 @@ struct schedule_ops pthread_sched_ops = {
        .thread_refl_fault = pth_thread_refl_fault,
        .thread_exited = pth_thread_exited,
        .thread_create = pth_thread_create,
+       .thread_bulk_runnable = pth_thread_bulk_runnable,
 };
 
 struct schedule_ops *sched_ops = &pthread_sched_ops;
@@ -375,6 +377,23 @@ static struct uthread *pth_thread_create(void *(*func)(void *), void *arg)
        return ret == 0 ? (struct uthread*)pth : NULL;
 }
 
+static void pth_thread_bulk_runnable(uth_sync_t *wakees)
+{
+       struct uthread *uth_i;
+       struct pthread_tcb *pth_i;
+
+       /* Amortize the lock grabbing over all restartees */
+       mcs_pdr_lock(&queue_lock);
+       while ((uth_i = __uth_sync_get_next(wakees))) {
+               pth_i = (struct pthread_tcb*)uth_i;
+               pth_i->state = PTH_RUNNABLE;
+               TAILQ_INSERT_TAIL(&ready_queue, pth_i, tq_next);
+               threads_ready++;
+       }
+       mcs_pdr_unlock(&queue_lock);
+       vcore_request_more(threads_ready);
+}
+
 /* Akaros pthread extensions / hacks */
 
 /* Careful using this - glibc and gcc are likely to use TLS without you knowing