uthread creation is now init, slims 2ls sched_ops
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 22 Aug 2011 23:28:30 +0000 (16:28 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:06 +0000 (17:36 -0700)
2LSs now create their threads however they want, and then call
uthread_init() on them.  Later, they must call uthread_cleanup().

Thread creation/destruction is more 'top-down' this way, and the 2LS
must be sure to call uthread_lib_init() in its initialization routines,
and call uthread_cleanup() when it is done with a uthread.

With great power comes great responsibility.

tests/mhello.c
tests/syscall.c
user/parlib/include/uthread.h
user/parlib/uthread.c
user/pthread/pthread.c

index 0b0e147..dc9b4f0 100644 (file)
@@ -26,30 +26,13 @@ struct event_queue *indirect_q;
 static void handle_generic(struct event_msg *ev_msg, unsigned int ev_type);
 
 void ghetto_vcore_entry(void);
-struct uthread *ghetto_init(void)
-{
-       struct uthread *uthread = malloc(sizeof(struct uthread));
-       memset(uthread, 0, sizeof(struct uthread));
-       return uthread;
-}
-
-struct uthread *ghetto_create(void (*func)(void), void *data)
-{
-       return ghetto_init();
-}
 
 struct schedule_ops ghetto_sched_ops = {
-       .sched_init = ghetto_init,
        .sched_entry = ghetto_vcore_entry,
-       .thread_create = ghetto_create,
 };
 struct schedule_ops *sched_ops = &ghetto_sched_ops;
 
 /* to trick uthread_create() */
-void dummy(void)
-{
-}
-
 int main(int argc, char** argv)
 {
        uint32_t vcoreid;
@@ -86,9 +69,10 @@ int main(int argc, char** argv)
        ev_q->ev_flags = EVENT_IPI | EVENT_NOMSG | EVENT_VCORE_APPRO;
        register_kevent_q(ev_q, EV_PREEMPT_PENDING);
 
-       /* Makes a thread for us, though we won't use it.  Just a hack to get into
+       /* Inits a thread for us, though we won't use it.  Just a hack to get into
         * _M mode.  Note this requests one vcore for us */
-       uthread_create(dummy, 0);
+       struct uthread dummy = {0};
+       uthread_lib_init(&dummy);
 
        if ((vcoreid = vcore_id())) {
                printf("Should never see me! (from vcore %d)\n", vcoreid);
@@ -133,6 +117,9 @@ int main(int argc, char** argv)
 
        printf("All Cores Done!\n", vcoreid);
        while(1); // manually kill from the monitor
+       /* since everyone should cleanup their uthreads, even if they don't plan on
+        * calling their code or want uthreads in the first place. <3 */
+       uthread_cleanup(&dummy);
        return 0;
 }
 
index f81348c..d13344b 100644 (file)
@@ -7,19 +7,14 @@
 #include <ros/bcq.h>
 #include <uthread.h>
 
+/* Deprecated, don't use this in any serious way */
+
 static void handle_syscall(struct event_msg *ev_msg, unsigned int ev_type);
 struct syscall sysc = {0};
 struct event_queue *ev_q;
-void *core0_tls = 0;
-
 void ghetto_vcore_entry(void);
-struct uthread *ghetto_init(void)
-{
-       return malloc(sizeof(struct uthread));
-}
 
 struct schedule_ops ghetto_sched_ops = {
-       .sched_init = ghetto_init,
        .sched_entry = ghetto_vcore_entry,
 };
 struct schedule_ops *sched_ops = &ghetto_sched_ops;
@@ -58,9 +53,10 @@ int main(int argc, char** argv)
        /* Note we don't need to set up event reception for any particular kevent.
         * The ev_q in the syscall said to send an IPI to vcore 0 which means an
         * EV_EVENT will be sent straight to vcore0. */
-       /* Need to save this somewhere that you can find it again when restarting
-        * core0 */
-       core0_tls = get_tls_desc(0);
+       /* Inits a thread for us, though we won't use it.  Just a hack to get into
+        * _M mode.  Note this requests one vcore for us */
+       struct uthread dummy = {0};
+       uthread_lib_init(&dummy);
        /* Need to save our floating point state somewhere (like in the
         * user_thread_tcb so it can be restarted too */
        enable_notifs(0);
index c467ebd..f5e367f 100644 (file)
@@ -29,12 +29,9 @@ extern __thread struct uthread *current_uthread;
 /* 2L-Scheduler operations.  Can be 0.  Examples in pthread.c. */
 struct schedule_ops {
        /* Functions supporting thread ops */
-       struct uthread *(*sched_init)(void);
        void (*sched_entry)(void);
-       struct uthread *(*thread_create)(void (*func)(void), void *);
        void (*thread_runnable)(struct uthread *);
        void (*thread_yield)(struct uthread *);
-       void (*thread_destroy)(struct uthread *);
        void (*thread_blockon_sysc)(struct syscall *);
        /* Functions event handling wants */
        void (*preempt_pending)(void);
@@ -42,16 +39,21 @@ struct schedule_ops {
 };
 extern struct schedule_ops *sched_ops;
 
+/* Call this, passing it a uthread representing thread0, from your 2LS init
+ * routines.  When it returns, you're in _M mode (thread0 on vcore0) */
+int uthread_lib_init(struct uthread *uthread);
+
 /* Functions to make/manage uthreads.  Can be called by functions such as
  * pthread_create(), which can wrap these with their own stuff (like attrs,
  * retvals, etc). */
 
-/* Creates a uthread.  Will pass udata to sched_ops's thread_create.  Func is
- * what gets run, and if you want args, wrap it (like pthread) */
-struct uthread *uthread_create(void (*func)(void), void *udata);
+/* Does the uthread initialization of a uthread that the caller created.  Call
+ * this whenever you are "starting over" with a thread. */
+void uthread_init(struct uthread *new_thread);
+/* Call this when you are done with a uthread, forever, but before you free it */
+void uthread_cleanup(struct uthread *uthread);
 void uthread_runnable(struct uthread *uthread);
 void uthread_yield(bool save_state);
-void uthread_destroy(struct uthread *uthread);
 /* Block the calling uthread on sysc until it makes progress or is done */
 void ros_syscall_blockon(struct syscall *sysc);
 
index 8873223..0498c49 100644 (file)
@@ -16,15 +16,17 @@ __thread struct uthread *current_uthread = 0;
 static int __uthread_allocate_tls(struct uthread *uthread);
 static void __uthread_free_tls(struct uthread *uthread);
 
-/* Gets called once out of uthread_create().  Can also do this in a ctor. */
-static int uthread_init(void)
+/* The real 2LS calls this, passing in a uthread representing thread0.  When it
+ * returns, you're in _M mode, still running thread0, on vcore0 */
+int uthread_lib_init(struct uthread *uthread)
 {
+       /* Make sure this only runs once */
+       static bool initialized = FALSE;
+       if (initialized)
+               return -1;
+       initialized = TRUE;
        /* Init the vcore system */
        assert(!vcore_init());
-       /* Bug if vcore init was called with no 2LS */
-       assert(sched_ops->sched_init);
-       /* Get thread 0's thread struct (2LS allocs it) */
-       struct uthread *uthread = sched_ops->sched_init();
        assert(uthread);
        /* Save a pointer to thread0's tls region (the glibc one) into its tcb */
        uthread->tls_desc = get_tls_desc(0);
@@ -72,21 +74,14 @@ void __attribute__((noreturn)) uthread_vcore_entry(void)
        assert(0);
 }
 
-/* Creates a uthread.  Will pass udata to sched_ops's thread_create.  For now,
- * the vcore/default 2ls code handles start routines and args.  Mostly because
- * this is used when initing a utf, which is vcore specific for now. */
-struct uthread *uthread_create(void (*func)(void), void *udata)
+/* Does the uthread initialization of a uthread that the caller created.  Call
+ * this whenever you are "starting over" with a thread. */
+void uthread_init(struct uthread *new_thread)
 {
-       /* First time through, init the uthread code (which makes a uthread out of
-        * thread0 / the current code.  Could move this to a ctor. */
-       static bool first = TRUE;
-       if (first) {
-               assert(!uthread_init());
-               first = FALSE;
-       }
+       /* don't remove this assert without dealing with 'caller' below.  if we want
+        * to call this while in vcore context, we'll need to handle the TLS
+        * swapping a little differently */
        assert(!in_vcore_context());
-       assert(sched_ops->thread_create);
-       struct uthread *new_thread = sched_ops->thread_create(func, udata);
        uint32_t vcoreid;
        assert(new_thread);
        new_thread->state = UT_CREATED;
@@ -118,7 +113,6 @@ struct uthread *uthread_create(void (*func)(void), void *udata)
                enable_notifs(vcoreid);
        wmb();
        caller->flags &= ~UTHREAD_DONT_MIGRATE;
-       return new_thread;
 }
 
 void uthread_runnable(struct uthread *uthread)
@@ -216,17 +210,15 @@ yield_return_path:
        printd("[U] Uthread %08p returning from a yield!\n", uthread);
 }
 
-/* Destroys the uthread.  If you want to destroy a currently running uthread,
- * you'll want something like pthread_exit(), which yields, and calls this from
- * its sched_ops yield. */
-void uthread_destroy(struct uthread *uthread)
+/* Cleans up the uthread (the stuff we did in uthread_init()).  If you want to
+ * destroy a currently running uthread, you'll want something like
+ * pthread_exit(), which yields, and calls this from its sched_ops yield. */
+void uthread_cleanup(struct uthread *uthread)
 {
        printd("[U] thread %08p on vcore %d is DYING!\n", uthread, vcore_id());
        uthread->state = UT_DYING;
        /* we alloc and manage the TLS, so lets get rid of it */
        __uthread_free_tls(uthread);
-       assert(sched_ops->thread_destroy);
-       sched_ops->thread_destroy(uthread);
 }
 
 /* Attempts to block on sysc, returning when it is done or progress has been
index 5a8f916..77d847c 100644 (file)
@@ -33,12 +33,9 @@ static int get_next_pid(void);
 static inline void spin_to_sleep(unsigned int spins, unsigned int *spun);
 
 /* Pthread 2LS operations */
-struct uthread *pth_init(void);
 void pth_sched_entry(void);
-struct uthread *pth_thread_create(void (*func)(void), void *udata);
 void pth_thread_runnable(struct uthread *uthread);
 void pth_thread_yield(struct uthread *uthread);
-void pth_thread_destroy(struct uthread *uthread);
 void pth_preempt_pending(void);
 void pth_spawn_thread(uintptr_t pc_start, void *data);
 void pth_blockon_sysc(struct syscall *sysc);
@@ -47,12 +44,9 @@ void pth_blockon_sysc(struct syscall *sysc);
 static void pth_handle_syscall(struct event_msg *ev_msg, unsigned int ev_type);
 
 struct schedule_ops pthread_sched_ops = {
-       pth_init,
        pth_sched_entry,
-       pth_thread_create,
        pth_thread_runnable,
        pth_thread_yield,
-       pth_thread_destroy,
        pth_blockon_sysc,
        0, /* pth_preempt_pending, */
        0, /* pth_spawn_thread, */
@@ -65,80 +59,6 @@ struct schedule_ops *sched_ops = &pthread_sched_ops;
 static void __pthread_free_stack(struct pthread_tcb *pt);
 static int __pthread_allocate_stack(struct pthread_tcb *pt);
 
-/* Do whatever init you want.  Return a uthread representing thread0 (int
- * main()) */
-struct uthread *pth_init(void)
-{
-       uintptr_t mmap_block;
-       struct mcs_lock_qnode local_qn = {0};
-       /* Tell the kernel where and how we want to receive events.  This is just an
-        * example of what to do to have a notification turned on.  We're turning on
-        * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
-        * send to vcore 0.  Note sys_self_notify will ignore the vcoreid pref.
-        * Also note that enable_kevent() is just an example, and you probably want
-        * to use parts of event.c to do what you want. */
-       enable_kevent(EV_USER_IPI, 0, EVENT_IPI);
-
-       /* Handle syscall events.  Using small ev_qs, with no internal ev_mbox. */
-       ev_handlers[EV_SYSCALL] = pth_handle_syscall;
-       /* Set up the per-vcore structs to track outstanding syscalls */
-       sysc_mgmt = malloc(sizeof(struct sysc_mgmt) * max_vcores());
-       assert(sysc_mgmt);
-#if 1   /* Independent ev_mboxes per vcore */
-       /* Get a block of pages for our per-vcore (but non-VCPD) ev_qs */
-       mmap_block = (uintptr_t)mmap(0, PGSIZE * 2 * max_vcores(),
-                                    PROT_WRITE | PROT_READ,
-                                    MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
-       assert(mmap_block);
-       /* Could be smarter and do this on demand (in case we don't actually want
-        * max_vcores()). */
-       for (int i = 0; i < max_vcores(); i++) {
-               /* Each vcore needs to point to a non-VCPD ev_q */
-               sysc_mgmt[i].ev_q = get_big_event_q_raw();
-               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
-               sysc_mgmt[i].ev_q->ev_vcore = i;
-               ucq_init_raw(&sysc_mgmt[i].ev_q->ev_mbox->ev_msgs, 
-                            mmap_block + (2 * i    ) * PGSIZE, 
-                            mmap_block + (2 * i + 1) * PGSIZE); 
-       }
-       /* Technically, we should munmap and free what we've alloc'd, but the
-        * kernel will clean it up for us when we exit. */
-#endif 
-#if 0   /* One global ev_mbox, separate ev_q per vcore */
-       struct event_mbox *sysc_mbox = malloc(sizeof(struct event_mbox));
-       uintptr_t two_pages = (uintptr_t)mmap(0, PGSIZE * 2, PROT_WRITE | PROT_READ,
-                                             MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
-       printd("Global ucq: %08p\n", &sysc_mbox->ev_msgs);
-       assert(sysc_mbox);
-       assert(two_pages);
-       memset(sysc_mbox, 0, sizeof(struct event_mbox));
-       ucq_init_raw(&sysc_mbox->ev_msgs, two_pages, two_pages + PGSIZE);
-       for (int i = 0; i < max_vcores(); i++) {
-               sysc_mgmt[i].ev_q = get_event_q();
-               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
-               sysc_mgmt[i].ev_q->ev_vcore = i;
-               sysc_mgmt[i].ev_q->ev_mbox = sysc_mbox;
-       }
-#endif
-
-       /* Create a pthread_tcb for the main thread */
-       pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
-       assert(t);
-       t->id = get_next_pid();
-       t->stacksize = USTACK_NUM_PAGES * PGSIZE;
-       t->stacktop = (void*)USTACKTOP;
-       t->detached = TRUE;
-       t->flags = 0;
-       t->finished = 0;
-       assert(t->id == 0);
-       /* Put the new pthread on the active queue */
-       mcs_lock_notifsafe(&queue_lock, &local_qn);
-       threads_active++;
-       TAILQ_INSERT_TAIL(&active_queue, t, next);
-       mcs_unlock_notifsafe(&queue_lock, &local_qn);
-       return (struct uthread*)t;
-}
-
 /* Called from vcore entry.  Options usually include restarting whoever was
  * running there before or running a new thread.  Events are handled out of
  * event.c (table of function pointers, stuff like that). */
@@ -186,36 +106,6 @@ static void __pthread_run(void)
        pthread_exit(me->start_routine(me->arg));
 }
 
-/* Responible for creating the uthread and initializing its user trap frame */
-struct uthread *pth_thread_create(void (*func)(void), void *udata)
-{
-       struct pthread_tcb *pthread;
-       pthread_attr_t *attr = (pthread_attr_t*)udata;
-       pthread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
-       assert(pthread);
-       pthread->stacksize = PTHREAD_STACK_SIZE;        /* default */
-       pthread->finished = 0;
-       pthread->flags = 0;
-       pthread->id = get_next_pid();
-       pthread->detached = FALSE;                              /* default */
-       /* Respect the attributes */
-       if (attr) {
-               if (attr->stacksize)                                    /* don't set a 0 stacksize */
-                       pthread->stacksize = attr->stacksize;
-               if (attr->detachstate == PTHREAD_CREATE_DETACHED)
-                       pthread->detached = TRUE;
-       }
-       /* allocate a stack */
-       if (__pthread_allocate_stack(pthread))
-               printf("We're fucked\n");
-       /* Set the u_tf to start up in __pthread_run, which will call the real
-        * start_routine and pass it the arg.  Note those aren't set until later in
-        * pthread_create(). */
-       init_user_tf(&pthread->uthread.utf, (uint32_t)__pthread_run, 
-                 (uint32_t)(pthread->stacktop));
-       return (struct uthread*)pthread;
-}
-
 void pth_thread_runnable(struct uthread *uthread)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
@@ -245,7 +135,15 @@ void pth_thread_yield(struct uthread *uthread)
        TAILQ_REMOVE(&active_queue, pthread, next);
        if (pthread->flags & PTHREAD_EXITING) {
                mcs_unlock_notifsafe(&queue_lock, &local_qn);
-               uthread_destroy(uthread);
+               /* Destroy the pthread */
+               uthread_cleanup(uthread);
+               /* Cleanup, mirroring pthread_create() */
+               __pthread_free_stack(pthread);
+               /* TODO: race on detach state */
+               if (pthread->detached)
+                       free(pthread);
+               else
+                       pthread->finished = 1;
        } else {
                /* Put it on the ready list (tail).  Don't do this until we are done
                 * completely with the thread, since it can be restarted somewhere else.
@@ -255,18 +153,6 @@ void pth_thread_yield(struct uthread *uthread)
                mcs_unlock_notifsafe(&queue_lock, &local_qn);
        }
 }
-       
-void pth_thread_destroy(struct uthread *uthread)
-{
-       struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       /* Cleanup, mirroring pth_thread_create() */
-       __pthread_free_stack(pthread);
-       /* TODO: race on detach state */
-       if (pthread->detached)
-               free(pthread);
-       else
-               pthread->finished = 1;
-}
 
 void pth_preempt_pending(void)
 {
@@ -382,15 +268,126 @@ int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize)
        return 0;
 }
 
-int pthread_create(pthread_t* thread, const pthread_attr_t* attr,
-                   void *(*start_routine)(void *), void* arg)
+/* Do whatever init you want.  At some point call uthread_lib_init() and pass it
+ * a uthread representing thread0 (int main()) */
+static int pthread_lib_init(void)
 {
-       struct pthread_tcb *pthread =
-              (struct pthread_tcb*)uthread_create(__pthread_run, (void*)attr);
-       if (!pthread)
+       /* Make sure this only runs once */
+       static bool initialized = FALSE;
+       if (initialized)
                return -1;
+       initialized = TRUE;
+       uintptr_t mmap_block;
+       struct mcs_lock_qnode local_qn = {0};
+       /* Create a pthread_tcb for the main thread */
+       pthread_t t = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
+       assert(t);
+       t->id = get_next_pid();
+       t->stacksize = USTACK_NUM_PAGES * PGSIZE;
+       t->stacktop = (void*)USTACKTOP;
+       t->detached = TRUE;
+       t->flags = 0;
+       t->finished = 0;
+       assert(t->id == 0);
+       /* Put the new pthread (thread0) on the active queue */
+       mcs_lock_notifsafe(&queue_lock, &local_qn);
+       threads_active++;
+       TAILQ_INSERT_TAIL(&active_queue, t, next);
+       mcs_unlock_notifsafe(&queue_lock, &local_qn);
+       /* Tell the kernel where and how we want to receive events.  This is just an
+        * example of what to do to have a notification turned on.  We're turning on
+        * USER_IPIs, posting events to vcore 0's vcpd, and telling the kernel to
+        * send to vcore 0.  Note sys_self_notify will ignore the vcoreid pref.
+        * Also note that enable_kevent() is just an example, and you probably want
+        * to use parts of event.c to do what you want. */
+       enable_kevent(EV_USER_IPI, 0, EVENT_IPI);
+
+       /* Handle syscall events. */
+       ev_handlers[EV_SYSCALL] = pth_handle_syscall;
+       /* Set up the per-vcore structs to track outstanding syscalls */
+       sysc_mgmt = malloc(sizeof(struct sysc_mgmt) * max_vcores());
+       assert(sysc_mgmt);
+#if 1   /* Independent ev_mboxes per vcore */
+       /* Get a block of pages for our per-vcore (but non-VCPD) ev_qs */
+       mmap_block = (uintptr_t)mmap(0, PGSIZE * 2 * max_vcores(),
+                                    PROT_WRITE | PROT_READ,
+                                    MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
+       assert(mmap_block);
+       /* Could be smarter and do this on demand (in case we don't actually want
+        * max_vcores()). */
+       for (int i = 0; i < max_vcores(); i++) {
+               /* Each vcore needs to point to a non-VCPD ev_q */
+               sysc_mgmt[i].ev_q = get_big_event_q_raw();
+               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
+               sysc_mgmt[i].ev_q->ev_vcore = i;
+               ucq_init_raw(&sysc_mgmt[i].ev_q->ev_mbox->ev_msgs, 
+                            mmap_block + (2 * i    ) * PGSIZE, 
+                            mmap_block + (2 * i + 1) * PGSIZE); 
+       }
+       /* Technically, we should munmap and free what we've alloc'd, but the
+        * kernel will clean it up for us when we exit. */
+#endif 
+#if 0   /* One global ev_mbox, separate ev_q per vcore */
+       struct event_mbox *sysc_mbox = malloc(sizeof(struct event_mbox));
+       uintptr_t two_pages = (uintptr_t)mmap(0, PGSIZE * 2, PROT_WRITE | PROT_READ,
+                                             MAP_POPULATE | MAP_ANONYMOUS, -1, 0);
+       printd("Global ucq: %08p\n", &sysc_mbox->ev_msgs);
+       assert(sysc_mbox);
+       assert(two_pages);
+       memset(sysc_mbox, 0, sizeof(struct event_mbox));
+       ucq_init_raw(&sysc_mbox->ev_msgs, two_pages, two_pages + PGSIZE);
+       for (int i = 0; i < max_vcores(); i++) {
+               sysc_mgmt[i].ev_q = get_event_q();
+               sysc_mgmt[i].ev_q->ev_flags = EVENT_IPI | EVENT_INDIR | EVENT_FALLBACK;
+               sysc_mgmt[i].ev_q->ev_vcore = i;
+               sysc_mgmt[i].ev_q->ev_mbox = sysc_mbox;
+       }
+#endif
+       /* Initialize the uthread code (we're in _M mode after this).  Doing this
+        * last so that all the event stuff is ready when we're in _M mode.  Not a
+        * big deal one way or the other.  Note that vcore_init() hasn't happened
+        * yet, so if a 2LS somehow wants to have its init stuff use things like
+        * vcore stacks or TLSs, we'll need to change this. */
+       assert(!uthread_lib_init((struct uthread*)t));
+       return 0;
+}
+
+int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
+                   void *(*start_routine)(void *), void *arg)
+{
+       static bool first = TRUE;
+       if (first) {
+               assert(!pthread_lib_init());
+               first = FALSE;
+       }
+       /* Create the actual thread */
+       struct pthread_tcb *pthread;
+       pthread = (pthread_t)calloc(1, sizeof(struct pthread_tcb));
+       assert(pthread);
+       pthread->stacksize = PTHREAD_STACK_SIZE;        /* default */
+       pthread->finished = 0;
+       pthread->flags = 0;
+       pthread->id = get_next_pid();
+       pthread->detached = FALSE;                              /* default */
+       /* Respect the attributes */
+       if (attr) {
+               if (attr->stacksize)                                    /* don't set a 0 stacksize */
+                       pthread->stacksize = attr->stacksize;
+               if (attr->detachstate == PTHREAD_CREATE_DETACHED)
+                       pthread->detached = TRUE;
+       }
+       /* allocate a stack */
+       if (__pthread_allocate_stack(pthread))
+               printf("We're fucked\n");
+       /* Set the u_tf to start up in __pthread_run, which will call the real
+        * start_routine and pass it the arg.  Note those aren't set until later in
+        * pthread_create(). */
+       init_user_tf(&pthread->uthread.utf, (uint32_t)__pthread_run, 
+                    (uint32_t)(pthread->stacktop));
        pthread->start_routine = start_routine;
        pthread->arg = arg;
+       /* Initializse the uthread */
+       uthread_init((struct uthread*)pthread);
        uthread_runnable((struct uthread*)pthread);
        *thread = pthread;
        return 0;