uthread_exit() replaced with uthread_destroy()
authorBarret Rhoden <brho@cs.berkeley.edu>
Thu, 4 Aug 2011 22:58:02 +0000 (15:58 -0700)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:36:06 +0000 (17:36 -0700)
For 2LSs that want to be able to destroy inactive threads from another
context, so that they can cache the.  This also simplifies uthread code
a bit.

user/parlib/include/uthread.h
user/parlib/uthread.c
user/pthread/pthread.c
user/pthread/pthread.h

index f9f8766..c467ebd 100644 (file)
@@ -5,7 +5,6 @@
 #include <ros/syscall.h>
 
 #define UTHREAD_DONT_MIGRATE           0x001 /* don't move to another vcore */
-#define UTHREAD_DYING                          0x002 /* uthread is exiting */
 
 /* Thread States */
 #define UT_CREATED     1
@@ -35,7 +34,7 @@ struct schedule_ops {
        struct uthread *(*thread_create)(void (*func)(void), void *);
        void (*thread_runnable)(struct uthread *);
        void (*thread_yield)(struct uthread *);
-       void (*thread_exit)(struct uthread *);
+       void (*thread_destroy)(struct uthread *);
        void (*thread_blockon_sysc)(struct syscall *);
        /* Functions event handling wants */
        void (*preempt_pending)(void);
@@ -51,8 +50,8 @@ extern struct schedule_ops *sched_ops;
  * what gets run, and if you want args, wrap it (like pthread) */
 struct uthread *uthread_create(void (*func)(void), void *udata);
 void uthread_runnable(struct uthread *uthread);
-void uthread_yield(void);
-void uthread_exit(void);
+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 718084f..8873223 100644 (file)
@@ -138,34 +138,22 @@ __uthread_yield(void)
        struct uthread *uthread = current_uthread;
        assert(in_vcore_context());
        assert(!notif_is_enabled(vcore_id()));
-       /* Do slightly different things depending on whether or not we're exiting.
-        * While it is tempting to get rid of the UTHREAD_DYING flag and have
-        * callers just set the thread state, we'd lose the ability to assert
-        * UT_RUNNING, which helps catch bugs. */
-       if (!(uthread->flags & UTHREAD_DYING)) {
-               uthread->flags &= ~UTHREAD_DONT_MIGRATE;
-               /* Determine if we're blocking on a syscall or just yielding.  Might end
-                * up doing this differently when/if we have more ways to yield. */
-               if (uthread->sysc) {
-                       uthread->state = UT_BLOCKED;
-                       assert(sched_ops->thread_blockon_sysc);
-                       sched_ops->thread_blockon_sysc(uthread->sysc);
-               } else { /* generic yield */
-                       uthread->state = UT_RUNNABLE;
-                       assert(sched_ops->thread_yield);
-                       /* 2LS will save the thread somewhere for restarting.  Later on,
-                        * we'll probably have a generic function for all sorts of waiting.
-                        */
-                       sched_ops->thread_yield(uthread);
-               }
-       } else { /* DYING */
-               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);
-               /* 2LS specific cleanup */
-               assert(sched_ops->thread_exit);
-               sched_ops->thread_exit(uthread);
+       /* Note: we no longer care if the thread is exiting, the 2LS will call
+        * uthread_destroy() */
+       uthread->flags &= ~UTHREAD_DONT_MIGRATE;
+       /* Determine if we're blocking on a syscall or just yielding.  Might end
+        * up doing this differently when/if we have more ways to yield. */
+       if (uthread->sysc) {
+               uthread->state = UT_BLOCKED;
+               assert(sched_ops->thread_blockon_sysc);
+               sched_ops->thread_blockon_sysc(uthread->sysc);
+       } else { /* generic yield */
+               uthread->state = UT_RUNNABLE;
+               assert(sched_ops->thread_yield);
+               /* 2LS will save the thread somewhere for restarting.  Later on,
+                * we'll probably have a generic function for all sorts of waiting.
+                */
+               sched_ops->thread_yield(uthread);
        }
        /* Leave the current vcore completely */
        current_uthread = NULL;
@@ -176,12 +164,12 @@ __uthread_yield(void)
 
 /* Calling thread yields.  Both exiting and yielding calls this, the difference
  * is the thread's state (in the flags). */
-void uthread_yield(void)
+void uthread_yield(bool save_state)
 {
        struct uthread *uthread = current_uthread;
        volatile bool yielding = TRUE; /* signal to short circuit when restarting */
        /* TODO: (HSS) Save silly state */
-       // if (!(uthread->flags & UTHREAD_DYING))
+       // if (save_state)
        //      save_fp_state(&t->as);
        assert(!in_vcore_context());
        assert(uthread->state == UT_RUNNING);
@@ -200,7 +188,7 @@ void uthread_yield(void)
        /* take the current state and save it into t->utf when this pthread
         * restarts, it will continue from right after this, see yielding is false,
         * and short ciruit the function.  Don't do this if we're dying. */
-       if (!(uthread->flags & UTHREAD_DYING))
+       if (save_state)
                save_ros_tf(&uthread->utf);
        /* Restart path doesn't matter if we're dying */
        if (!yielding)
@@ -228,12 +216,17 @@ yield_return_path:
        printd("[U] Uthread %08p returning from a yield!\n", uthread);
 }
 
-/* Exits from the uthread.  Tempting to get rid of this function, but we need to
- * manage the flags so we know to clean up the TLS and stuff later. */
-void uthread_exit(void)
+/* 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)
 {
-       current_uthread->flags |= UTHREAD_DYING;
-       uthread_yield();
+       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
@@ -256,7 +249,7 @@ void ros_syscall_blockon(struct syscall *sysc)
        /* So yield knows we are blocking on something */
        assert(current_uthread);
        current_uthread->sysc = sysc;
-       uthread_yield();
+       uthread_yield(TRUE);
 }
 
 /* Runs whatever thread is vcore's current_uthread */
index 5308d59..dd3309e 100644 (file)
@@ -38,7 +38,7 @@ 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_exit(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);
@@ -52,7 +52,7 @@ struct schedule_ops pthread_sched_ops = {
        pth_thread_create,
        pth_thread_runnable,
        pth_thread_yield,
-       pth_thread_exit,
+       pth_thread_destroy,
        pth_blockon_sysc,
        0, /* pth_preempt_pending, */
        0, /* pth_spawn_thread, */
@@ -195,6 +195,8 @@ struct uthread *pth_thread_create(void (*func)(void), void *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 */
@@ -237,28 +239,27 @@ void pth_thread_yield(struct uthread *uthread)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
        struct mcs_lock_qnode local_qn = {0};
-       /* Take from the active list, and put on the ready list (tail).  Don't do
-        * this until we are done completely with the thread, since it can be
-        * restarted somewhere else. */
+       /* Remove from the active list, whether exiting or yielding.  We're holding
+        * the lock throughout both list modifications (if applicable). */
        mcs_lock_notifsafe(&queue_lock, &local_qn);
        threads_active--;
        TAILQ_REMOVE(&active_queue, pthread, next);
-       threads_ready++;
-       TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
-       mcs_unlock_notifsafe(&queue_lock, &local_qn);
+       if (pthread->flags & PTHREAD_EXITING) {
+               mcs_unlock_notifsafe(&queue_lock, &local_qn);
+               uthread_destroy(uthread);
+       } 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.
+                * */
+               threads_ready++;
+               TAILQ_INSERT_TAIL(&ready_queue, pthread, next);
+               mcs_unlock_notifsafe(&queue_lock, &local_qn);
+       }
 }
-
-/* Thread is exiting, do your 2LS specific stuff.  You're in vcore context.
- * Don't use the thread's TLS or stack or anything. */
-void pth_thread_exit(struct uthread *uthread)
+       
+void pth_thread_destroy(struct uthread *uthread)
 {
        struct pthread_tcb *pthread = (struct pthread_tcb*)uthread;
-       struct mcs_lock_qnode local_qn = {0};
-       /* Remove from the active runqueue */
-       mcs_lock_notifsafe(&queue_lock, &local_qn);
-       threads_active--;
-       TAILQ_REMOVE(&active_queue, pthread, next);
-       mcs_unlock_notifsafe(&queue_lock, &local_qn);
        /* Cleanup, mirroring pth_thread_create() */
        __pthread_free_stack(pthread);
        /* TODO: race on detach state */
@@ -415,7 +416,7 @@ int pthread_join(pthread_t thread, void** retval)
 
 int pthread_yield(void)
 {
-       uthread_yield();
+       uthread_yield(TRUE);
        return 0;
 }
 
@@ -594,7 +595,9 @@ void pthread_exit(void *ret)
 {
        struct pthread_tcb *pthread = pthread_self();
        pthread->retval = ret;
-       uthread_exit();
+       /* So our pth_thread_yield knows we want to exit */
+       pthread->flags |= PTHREAD_EXITING;
+       uthread_yield(FALSE);
 }
 
 int pthread_once(pthread_once_t* once_control, void (*init_routine)(void))
index 49553d7..6902cbf 100644 (file)
   extern "C" {
 #endif
 
+/* Flags */
+#define PTHREAD_EXITING                0x001
+
 /* Pthread struct.  First has to be the uthread struct, which the vcore code
  * will access directly (as if pthread_tcb is a struct uthread). */
 struct pthread_tcb {
        struct uthread uthread;
        TAILQ_ENTRY(pthread_tcb) next;
-       int finished;
+       int finished;   /* TODO: merge this with flags */
+       int flags;
        bool detached;
        uint32_t id;
        uint32_t stacksize;