parlib: Make sync objects static (XCC)
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 20 Apr 2017 13:49:35 +0000 (09:49 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
It turns out that sync objects need to be statically allocated and capable
of initialization without malloc.  We're going to need to use uthread
mutexes for Glibc's locks (libc-lock.h), which get used for malloc.  Thus
we can't call back into malloc. (recursion).

Rebuild the world (especially GCC).

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

index 1d99273..343ef92 100644 (file)
@@ -70,23 +70,32 @@ TAILQ_HEAD(uth_tailq, uthread);
 
 extern __thread struct uthread *current_uthread;
 
-
-/* This struct is undefined.  We use it instead of void * so we can get
- * compiler warnings if someone passes the wrong pointer type.  Internally, 2LSs
- * and the default implementation use another object type. */
-typedef struct __uth_sync_opaque * uth_sync_t;
+/* This struct is a blob of sufficient storage to be whatever a 2LS wants for
+ * its thread list structure (e.g., TAILQ, priority queue, RB tree, etc).
+ * Internally, 2LSs and the default implementation use another object type.
+ *
+ * If a 2LS overrides the sync ops and uses its own synchronization, it can
+ * either use the uthread->sync_next field, or add its own field to its thread
+ * structure.
+ *
+ * If we need to increase the size, then do a full rebuild (with a make clean)
+ * of the toolchain.  libgomp and probably c++ threads care about the size of
+ * objects that contain uth_sync_t. */
+typedef struct __uth_sync_opaque {
+       uint8_t                                         foo[sizeof(uintptr_t) * 2];
+} __attribute__ ((aligned(sizeof(uintptr_t)))) uth_sync_t;
 
 /* 2LS-independent synchronization code (e.g. uthread mutexes) uses these
  * helpers to access 2LS-specific functions.
  *
  * Note the spinlock associated with the higher-level sync primitive is held for
  * these (where applicable). */
-uth_sync_t __uth_sync_alloc(void);
-void __uth_sync_free(uth_sync_t sync);
-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_init(uth_sync_t *sync);
+void __uth_sync_destroy(uth_sync_t *sync);
+struct uthread *__uth_sync_get_next(uth_sync_t *sync);
+bool __uth_sync_get_uth(uth_sync_t *sync, struct uthread *uth);
 /* 2LSs that use default sync objs will call this in their has_blocked op. */
-void __uth_default_sync_enqueue(struct uthread *uth, uth_sync_t sync);
+void __uth_default_sync_enqueue(struct uthread *uth, uth_sync_t *sync);
 
 /* 2L-Scheduler operations.  Examples in pthread.c. */
 struct schedule_ops {
@@ -96,16 +105,15 @@ struct schedule_ops {
        void (*thread_runnable)(struct uthread *);
        void (*thread_paused)(struct uthread *);
        void (*thread_blockon_sysc)(struct uthread *, void *);
-       void (*thread_has_blocked)(struct uthread *, uth_sync_t, int);
+       void (*thread_has_blocked)(struct uthread *, uth_sync_t *, int);
        void (*thread_refl_fault)(struct uthread *, struct user_context *);
        void (*thread_exited)(struct uthread *);
        struct uthread *(*thread_create)(void *(*)(void *), void *);
        /**** Defining these functions is optional. ****/
-       uth_sync_t (*sync_alloc)(void);
-       void (*sync_free)(uth_sync_t);
-       struct uthread *(*sync_get_next)(uth_sync_t);
-       bool (*sync_get_uth)(uth_sync_t, struct uthread *);
-       /* Functions event handling wants */
+       void (*sync_init)(uth_sync_t *);
+       void (*sync_destroy)(uth_sync_t *);
+       struct uthread *(*sync_get_next)(uth_sync_t *);
+       bool (*sync_get_uth)(uth_sync_t *, struct uthread *);
        void (*preempt_pending)(void);
 };
 extern struct schedule_ops *sched_ops;
@@ -159,7 +167,7 @@ void uthread_yield(bool save_state, void (*yield_func)(struct uthread*, void*),
 void uthread_sleep(unsigned int seconds);
 void uthread_usleep(unsigned int usecs);
 void __attribute__((noreturn)) uthread_sleep_forever(void);
-void uthread_has_blocked(struct uthread *uthread, uth_sync_t sync, int flags);
+void uthread_has_blocked(struct uthread *uthread, uth_sync_t *sync, int flags);
 void uthread_paused(struct uthread *uthread);
 
 /* Utility functions */
@@ -220,8 +228,8 @@ typedef struct uth_rwlock uth_rwlock_t;
 
 struct uth_semaphore {
        struct spin_pdr_lock            lock;
-       uth_sync_t                                      sync_obj;
        unsigned int                            count;
+       uth_sync_t                                      sync_obj;
        parlib_once_t                           once_ctl;
 };
 #define UTH_SEMAPHORE_INIT(n) { .once_ctl = PARLIB_ONCE_INIT, .count = (n) }
index 58ed079..314be10 100644 (file)
@@ -9,6 +9,7 @@
 #include <sys/queue.h>
 #include <parlib/spinlock.h>
 #include <parlib/alarm.h>
+#include <parlib/assert.h>
 #include <malloc.h>
 
 struct timeout_blob {
@@ -25,7 +26,7 @@ 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))
+       if (__uth_sync_get_uth(blob->sync_ptr, blob->uth))
                blob->timed_out = TRUE;
        spin_pdr_unlock(blob->lock_ptr);
        if (blob->timed_out)
@@ -60,7 +61,7 @@ static void __uth_semaphore_init(void *arg)
        struct uth_semaphore *sem = (struct uth_semaphore*)arg;
 
        spin_pdr_init(&sem->lock);
-       sem->sync_obj = __uth_sync_alloc();
+       __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()). */
 }
@@ -78,7 +79,7 @@ void uth_semaphore_init(uth_semaphore_t *sem, unsigned int count)
 /* Undoes whatever was done in init. */
 void uth_semaphore_destroy(uth_semaphore_t *sem)
 {
-       __uth_sync_free(sem->sync_obj);
+       __uth_sync_destroy(&sem->sync_obj);
 }
 
 uth_semaphore_t *uth_semaphore_alloc(unsigned int count)
@@ -107,7 +108,7 @@ 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, &sem->sync_obj, UTH_EXT_BLK_MUTEX);
        spin_pdr_unlock(&sem->lock);
 }
 
@@ -170,7 +171,7 @@ void uth_semaphore_up(uth_semaphore_t *sem)
        /* 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);
+       uth = __uth_sync_get_next(&sem->sync_obj);
        /* If there was a waiter, we pass our resource/count to them. */
        if (!uth)
                sem->count++;
@@ -348,7 +349,7 @@ static void __uth_cond_var_init(void *arg)
        struct uth_cond_var *cv = (struct uth_cond_var*)arg;
 
        spin_pdr_init(&cv->lock);
-       cv->sync_obj = __uth_sync_alloc();
+       __uth_sync_init(&cv->sync_obj);
 }
 
 void uth_cond_var_init(uth_cond_var_t *cv)
@@ -359,7 +360,7 @@ void uth_cond_var_init(uth_cond_var_t *cv)
 
 void uth_cond_var_destroy(uth_cond_var_t *cv)
 {
-       __uth_sync_free(cv->sync_obj);
+       __uth_sync_destroy(&cv->sync_obj);
 }
 
 uth_cond_var_t *uth_cond_var_alloc(void)
@@ -395,7 +396,7 @@ 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, &cv->sync_obj, UTH_EXT_BLK_MUTEX);
        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
@@ -532,7 +533,7 @@ 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 = __uth_sync_get_next(cv->sync_obj);
+       uth = __uth_sync_get_next(&cv->sync_obj);
        spin_pdr_unlock(&cv->lock);
        if (uth)
                uthread_runnable(uth);
@@ -547,7 +548,7 @@ void uth_cond_var_broadcast(uth_cond_var_t *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))) {
+       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);
        }
@@ -568,8 +569,8 @@ static void __uth_rwlock_init(void *arg)
        spin_pdr_init(&rwl->lock);
        rwl->nr_readers = 0;
        rwl->has_writer = FALSE;
-       rwl->readers = __uth_sync_alloc();
-       rwl->writers = __uth_sync_alloc();
+       __uth_sync_init(&rwl->readers);
+       __uth_sync_init(&rwl->writers);
 }
 
 void uth_rwlock_init(uth_rwlock_t *rwl)
@@ -580,8 +581,8 @@ void uth_rwlock_init(uth_rwlock_t *rwl)
 
 void uth_rwlock_destroy(uth_rwlock_t *rwl)
 {
-       __uth_sync_free(rwl->readers);
-       __uth_sync_free(rwl->writers);
+       __uth_sync_destroy(&rwl->readers);
+       __uth_sync_destroy(&rwl->writers);
 }
 
 uth_rwlock_t *uth_rwlock_alloc(void)
@@ -606,7 +607,7 @@ 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, &rwl->readers, UTH_EXT_BLK_MUTEX);
        spin_pdr_unlock(&rwl->lock);
 }
 
@@ -641,7 +642,7 @@ 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, &rwl->writers, UTH_EXT_BLK_MUTEX);
        spin_pdr_unlock(&rwl->lock);
 }
 
@@ -680,12 +681,12 @@ static void __rw_unlock_writer(struct uth_rwlock *rwl,
 {
        struct uthread *uth;
 
-       uth = __uth_sync_get_next(rwl->writers);
+       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))) {
+               while ((uth = __uth_sync_get_next(&rwl->readers))) {
                        TAILQ_INSERT_TAIL(restartees, uth, sync_next);
                        rwl->nr_readers++;
                }
@@ -699,7 +700,7 @@ static void __rw_unlock_reader(struct uth_rwlock *rwl,
 
        rwl->nr_readers--;
        if (!rwl->nr_readers) {
-               uth = __uth_sync_get_next(rwl->writers);
+               uth = __uth_sync_get_next(&rwl->writers);
                if (uth) {
                        TAILQ_INSERT_TAIL(restartees, uth, sync_next);
                        rwl->has_writer = TRUE;
@@ -727,25 +728,22 @@ void uth_rwlock_unlock(uth_rwlock_t *rwl)
 
 /************** Default Sync Obj Implementation **************/
 
-static uth_sync_t uth_default_sync_alloc(void)
+static void uth_default_sync_init(uth_sync_t *sync)
 {
-       struct uth_tailq *tq;
+       struct uth_tailq *tq = (struct uth_tailq*)sync;
 
-       tq = malloc(sizeof(struct uth_tailq));
-       assert(tq);
+       parlib_static_assert(sizeof(struct uth_tailq) <= sizeof(uth_sync_t));
        TAILQ_INIT(tq);
-       return (uth_sync_t)tq;
 }
 
-static void uth_default_sync_free(uth_sync_t sync)
+static void uth_default_sync_destroy(uth_sync_t *sync)
 {
        struct uth_tailq *tq = (struct uth_tailq*)sync;
 
        assert(TAILQ_EMPTY(tq));
-       free(tq);
 }
 
-static struct uthread *uth_default_sync_get_next(uth_sync_t sync)
+static struct uthread *uth_default_sync_get_next(uth_sync_t *sync)
 {
        struct uth_tailq *tq = (struct uth_tailq*)sync;
        struct uthread *first;
@@ -756,7 +754,7 @@ static struct uthread *uth_default_sync_get_next(uth_sync_t sync)
        return first;
 }
 
-static bool uth_default_sync_get_uth(uth_sync_t sync, struct uthread *uth)
+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;
@@ -773,33 +771,35 @@ static bool uth_default_sync_get_uth(uth_sync_t sync, struct uthread *uth)
 /************** External uthread sync interface **************/
 
 /* 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)
+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);
 }
 
-/* Called by 2LS-independent sync code when a sync object is created. */
-uth_sync_t __uth_sync_alloc(void)
+/* Called by 2LS-independent sync code when a sync object needs initialized. */
+void __uth_sync_init(uth_sync_t *sync)
 {
-       if (sched_ops->sync_alloc)
-               return sched_ops->sync_alloc();
-       return uth_default_sync_alloc();
+       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_free(uth_sync_t sync)
+void __uth_sync_destroy(uth_sync_t *sync)
 {
-       if (sched_ops->sync_free) {
-               sched_ops->sync_free(sync);
+       if (sched_ops->sync_destroy) {
+               sched_ops->sync_destroy(sync);
                return;
        }
-       uth_default_sync_free(sync);
+       uth_default_sync_destroy(sync);
 }
 
 /* Called by 2LS-independent sync code when a thread needs to be woken. */
-struct uthread *__uth_sync_get_next(uth_sync_t sync)
+struct uthread *__uth_sync_get_next(uth_sync_t *sync)
 {
        if (sched_ops->sync_get_next)
                return sched_ops->sync_get_next(sync);
@@ -808,7 +808,7 @@ struct uthread *__uth_sync_get_next(uth_sync_t 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)
+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);
index aed2dce..203a189 100644 (file)
@@ -22,14 +22,14 @@ static void thread0_thread_blockon_sysc(struct uthread *uthread, void *sysc);
 static void thread0_thread_refl_fault(struct uthread *uth,
                                       struct user_context *ctx);
 static void thread0_thread_runnable(struct uthread *uth);
-static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t sync,
+static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t *sync,
                                        int flags);
 static void thread0_thread_exited(struct uthread *uth);
 static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg);
-static uth_sync_t thread0_sync_alloc(void);
-static void thread0_sync_free(uth_sync_t);
-static struct uthread *thread0_sync_get_next(uth_sync_t);
-static bool thread0_sync_get_uth(uth_sync_t s, struct uthread *uth);
+static void thread0_sync_init(uth_sync_t *s);
+static void thread0_sync_destroy(uth_sync_t *s);
+static struct uthread *thread0_sync_get_next(uth_sync_t *s);
+static bool thread0_sync_get_uth(uth_sync_t *s, struct uthread *uth);
 
 /* externed into uthread.c */
 struct schedule_ops thread0_2ls_ops = {
@@ -41,8 +41,8 @@ struct schedule_ops thread0_2ls_ops = {
        .thread_has_blocked = thread0_thread_has_blocked,
        .thread_exited = thread0_thread_exited,
        .thread_create = thread0_thread_create,
-       .sync_alloc = thread0_sync_alloc,
-       .sync_free = thread0_sync_free,
+       .sync_init = thread0_sync_init,
+       .sync_destroy = thread0_sync_destroy,
        .sync_get_next = thread0_sync_get_next,
        .sync_get_uth = thread0_sync_get_uth,
 };
@@ -146,7 +146,7 @@ static void thread0_thread_runnable(struct uthread *uth)
        thread0_info.is_blocked = FALSE;
 }
 
-static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t sync,
+static void thread0_thread_has_blocked(struct uthread *uth, uth_sync_t *sync,
                                        int flags)
 {
        assert(!thread0_info.is_blocked);
@@ -165,16 +165,16 @@ static struct uthread *thread0_thread_create(void *(*func)(void *), void *arg)
        panic("Thread0 sched asked to create more threads!");
 }
 
-static uth_sync_t thread0_sync_alloc(void)
+static void thread0_sync_init(uth_sync_t *s)
 {
-       return (void*)0xf00baa;
+       memset(s, 0x5a, sizeof(uth_sync_t));
 }
 
-static void thread0_sync_free(uth_sync_t s)
+static void thread0_sync_destroy(uth_sync_t *s)
 {
 }
 
-static struct uthread *thread0_sync_get_next(uth_sync_t s)
+static struct uthread *thread0_sync_get_next(uth_sync_t *s)
 {
        if (thread0_info.is_blocked) {
                /* Note we don't clear is_blocked.  Runnable does that, which should be
@@ -185,7 +185,7 @@ static struct uthread *thread0_sync_get_next(uth_sync_t s)
        }
 }
 
-static bool thread0_sync_get_uth(uth_sync_t s, struct uthread *uth)
+static bool thread0_sync_get_uth(uth_sync_t *s, struct uthread *uth)
 {
        assert(uth == thread0_uth);
        if (thread0_info.is_blocked) {
index eaf8d42..a1836f0 100644 (file)
@@ -386,7 +386,7 @@ void uthread_runnable(struct uthread *uthread)
  * 2LS should do regarding sync; it'll be told when the thread is runnable.
  *
  * AKA: obviously_a_uthread_has_blocked_in_lincoln_park() */
-void uthread_has_blocked(struct uthread *uthread, uth_sync_t sync, int flags)
+void uthread_has_blocked(struct uthread *uthread, uth_sync_t *sync, int flags)
 {
        assert(sched_ops->thread_has_blocked);
        sched_ops->thread_has_blocked(uthread, sync, flags);
index a79eb97..ceafca2 100644 (file)
@@ -42,8 +42,8 @@ static void pth_sched_entry(void);
 static void pth_thread_runnable(struct uthread *uthread);
 static void pth_thread_paused(struct uthread *uthread);
 static void pth_thread_blockon_sysc(struct uthread *uthread, void *sysc);
-static void pth_thread_has_blocked(struct uthread *uthread, uth_sync_t sync_obj,
-                                   int flags);
+static void pth_thread_has_blocked(struct uthread *uthread,
+                                   uth_sync_t *sync_obj, int flags);
 static void pth_thread_refl_fault(struct uthread *uth,
                                   struct user_context *ctx);
 static void pth_thread_exited(struct uthread *uth);
@@ -249,8 +249,8 @@ static void pth_thread_blockon_sysc(struct uthread *uthread, void *syscall)
        /* GIANT WARNING: do not touch the thread after this point. */
 }
 
-static void pth_thread_has_blocked(struct uthread *uthread, uth_sync_t sync_obj,
-                                   int flags)
+static void pth_thread_has_blocked(struct uthread *uthread,
+                                   uth_sync_t *sync_obj, int flags)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
 
index fd5abb1..744cb39 100644 (file)
@@ -37,7 +37,7 @@ static void vmm_sched_entry(void);
 static void vmm_thread_runnable(struct uthread *uth);
 static void vmm_thread_paused(struct uthread *uth);
 static void vmm_thread_blockon_sysc(struct uthread *uth, void *sysc);
-static void vmm_thread_has_blocked(struct uthread *uth, uth_sync_t sync_obj,
+static void vmm_thread_has_blocked(struct uthread *uth, uth_sync_t *sync_obj,
                                    int flags);
 static void vmm_thread_refl_fault(struct uthread *uth,
                                   struct user_context *ctx);
@@ -274,7 +274,7 @@ static void vmm_thread_blockon_sysc(struct uthread *uth, void *syscall)
        /* GIANT WARNING: do not touch the thread after this point. */
 }
 
-static void vmm_thread_has_blocked(struct uthread *uth, uth_sync_t sync_obj,
+static void vmm_thread_has_blocked(struct uthread *uth, uth_sync_t *sync_obj,
                                    int flags)
 {
        /* The thread blocked on something like a mutex.  It's not runnable, so we