parlib: Add uthread_create()
authorBarret Rhoden <brho@cs.berkeley.edu>
Wed, 12 Apr 2017 16:35:30 +0000 (12:35 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Wed, 3 May 2017 16:13:02 +0000 (12:13 -0400)
And the corresponding 2LS op.  uthread_create() creates and runs a generic
thread that runs a function - similar to pthread_create(), but it works
regardless of the 2LS being used.

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

index 5e65643..eb96f79 100644 (file)
@@ -98,6 +98,7 @@ struct schedule_ops {
        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);
@@ -139,6 +140,10 @@ struct uth_join_request {
  * created.  Call this whenever you are "starting over" with a thread.  Pass in
  * attr, if you want to override any defaults. */
 void uthread_init(struct uthread *new_thread, struct uth_thread_attr *attr);
+/* uthread_create() is a front-end for getting the 2LS to make and run a thread
+ * appropriate for running func(arg) in the GCC/glibc environment.  The thread
+ * will have TLS and not be detached. */
+struct uthread *uthread_create(void *(*func)(void *), void *arg);
 void uthread_detach(struct uthread *uth);
 void uthread_join(struct uthread *uth, void **retval_loc);
 void uthread_join_arr(struct uth_join_request reqs[], size_t nr_req);
index 1def58c..d973c41 100644 (file)
@@ -24,6 +24,7 @@ static void thread0_thread_runnable(struct uthread *uth);
 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);
@@ -38,6 +39,7 @@ struct schedule_ops thread0_2ls_ops = {
        .thread_paused = thread0_thread_runnable,
        .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_get_next = thread0_sync_get_next,
@@ -157,6 +159,11 @@ static void thread0_thread_exited(struct uthread *uth)
        assert(0);
 }
 
+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)
 {
        return (void*)0xf00baa;
index faa425c..70ef9b4 100644 (file)
@@ -1214,6 +1214,11 @@ bool uth_2ls_is_multithreaded(void)
        return sched_ops != &thread0_2ls_ops;
 }
 
+struct uthread *uthread_create(void *(*func)(void *), void *arg)
+{
+       return sched_ops->thread_create(func, arg);
+}
+
 /* Who does the thread_exited callback (2LS-specific cleanup)?  It depends.  If
  * the thread exits first, then join/detach does it.  o/w, the exit path does.
  *
index d09f901..5a548ac 100644 (file)
@@ -45,6 +45,7 @@ static void pth_thread_has_blocked(struct uthread *uthread, uth_sync_t sync_obj,
 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);
 
 /* Event Handlers */
 static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type,
@@ -58,6 +59,7 @@ struct schedule_ops pthread_sched_ops = {
        .thread_has_blocked = pth_thread_has_blocked,
        .thread_refl_fault = pth_thread_refl_fault,
        .thread_exited = pth_thread_exited,
+       .thread_create = pth_thread_create,
 };
 
 /* Static helpers */
@@ -352,8 +354,21 @@ static void pth_thread_exited(struct uthread *uth)
                exit(0);
 }
 
+/* Careful, if someone used the pthread_need_tls() hack to turn off TLS, it will
+ * also be turned off for these threads. */
+static struct uthread *pth_thread_create(void *(*func)(void *), void *arg)
+{
+       struct pthread_tcb *pth;
+       int ret;
+
+       ret = pthread_create(&pth, NULL, func, arg);
+       return ret == 0 ? (struct uthread*)pth : NULL;
+}
+
 /* Akaros pthread extensions / hacks */
 
+/* Careful using this - glibc and gcc are likely to use TLS without you knowing
+ * it. */
 void pthread_need_tls(bool need)
 {
        need_tls = need;
index b5c5310..fd5abb1 100644 (file)
@@ -42,6 +42,7 @@ static void vmm_thread_has_blocked(struct uthread *uth, uth_sync_t sync_obj,
 static void vmm_thread_refl_fault(struct uthread *uth,
                                   struct user_context *ctx);
 static void vmm_thread_exited(struct uthread *uth);
+static struct uthread *vmm_thread_create(void *(*func)(void *), void *arg);
 
 struct schedule_ops vmm_sched_ops = {
        .sched_entry = vmm_sched_entry,
@@ -51,6 +52,7 @@ struct schedule_ops vmm_sched_ops = {
        .thread_has_blocked = vmm_thread_has_blocked,
        .thread_refl_fault = vmm_thread_refl_fault,
        .thread_exited = vmm_thread_exited,
+       .thread_create = vmm_thread_create,
 };
 
 /* Helpers */
@@ -499,11 +501,12 @@ static void __task_thread_run(void)
        uth_2ls_thread_exit(tth->func(tth->arg));
 }
 
-struct task_thread *vmm_run_task(struct virtual_machine *vm,
-                                 void *(*func)(void *), void *arg)
+/* Helper, creates and starts a task thread. */
+static struct task_thread *__vmm_run_task(struct virtual_machine *vm,
+                                          void *(*func)(void *), void *arg,
+                                          struct uth_thread_attr *tth_attr)
 {
        struct task_thread *tth;
-       struct uth_thread_attr tth_attr = {.want_tls = TRUE, .detached = TRUE};
 
        tth = (struct task_thread*)alloc_vmm_thread(vm, VMM_THREAD_TASK);
        if (!tth)
@@ -518,12 +521,32 @@ struct task_thread *vmm_run_task(struct virtual_machine *vm,
        tth->arg = arg;
        init_user_ctx(&tth->uthread.u_ctx, (uintptr_t)&__task_thread_run,
                      (uintptr_t)(tth->stacktop));
-       uthread_init((struct uthread*)tth, &tth_attr);
+       uthread_init((struct uthread*)tth, tth_attr);
        acct_thread_unblocked((struct vmm_thread*)tth);
        enqueue_vmm_thread((struct vmm_thread*)tth);
        return tth;
 }
 
+struct task_thread *vmm_run_task(struct virtual_machine *vm,
+                                 void *(*func)(void *), void *arg)
+{
+       struct uth_thread_attr tth_attr = {.want_tls = TRUE, .detached = TRUE};
+
+       return __vmm_run_task(vm, func, arg, &tth_attr);
+}
+
+static struct uthread *vmm_thread_create(void *(*func)(void *), void *arg)
+{
+       struct uth_thread_attr tth_attr = {.want_tls = TRUE, .detached = FALSE};
+       struct task_thread *tth;
+
+       /* It's OK to not have a VM for a generic thread */
+       tth = __vmm_run_task(NULL, func, arg, &tth_attr);
+       /* But just in case, let's poison it */
+       ((struct vmm_thread*)tth)->vm = (void*)0xdeadbeef;
+       return (struct uthread*)tth;
+}
+
 /* Helpers for tracking nr_unblk_* threads. */
 static void acct_thread_blocked(struct vmm_thread *vth)
 {