Complete rewrite of c3po for multicore
authorKevin Klues <klueska@cs.berkeley.edu>
Wed, 2 Mar 2011 01:15:31 +0000 (17:15 -0800)
committerKevin Klues <klueska@cs.berkeley.edu>
Thu, 3 Nov 2011 00:35:59 +0000 (17:35 -0700)
I'm trying to factor the code in such a way that we can use a common code base
for both ROS and linux.  In the current revision, everything only runs on ROS,
but soon all "vcore" specific code can be implemented for linux by spawning an
underlying linux task and scheduling user threads on top of them.

Currenlty the code only runs reliably if we simply jump to multicore mode, but
don't put any more requests in for vcores (i.e. vcore_request is only called
once, asking for one core).  We are working through the bugs to figure out why
this is the case, but most signs point to either a kernel bug, or a low level
userspace bug, beneath the library code implemented here.  the test app for
c3po is still under tests/c3po/c3po_test.c

18 files changed:
tests/c3po/c3po_test.c
user/c3po/Makefile
user/c3po/coro/Makefrag [deleted file]
user/c3po/coro/coro.c [deleted file]
user/c3po/coro/coro.h [deleted file]
user/c3po/threads/events.c [deleted file]
user/c3po/threads/mutex.c
user/c3po/threads/sched_global_rr.c
user/c3po/threads/threadlib.c
user/c3po/threads/threadlib.h
user/c3po/threads/threadlib_internal.h
user/c3po/threads/ucontext.c [new file with mode: 0644]
user/c3po/threads/ucontext.h [new file with mode: 0644]
user/c3po/threads/vcore.c [new file with mode: 0644]
user/c3po/util/clock.c
user/c3po/util/config.c
user/c3po/util/debug.c
user/c3po/util/debug.h

index 9e2fbe0..7c50189 100644 (file)
@@ -5,9 +5,8 @@
 #include <unistd.h>
 
 pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
-#define vcore_id() 0
-//#define printf_safe(...) {}
-#define printf_safe(...) \
+#define printf_safe(...) {}
+//#define printf_safe(...) \
        pthread_mutex_lock(&lock); \
        printf(__VA_ARGS__); \
        pthread_mutex_unlock(&lock);
@@ -16,7 +15,7 @@ pthread_t t1;
 pthread_t t2;
 pthread_t t3;
 
-#define NUM_TEST_THREADS 1000
+#define NUM_TEST_THREADS 100
 
 pthread_t my_threads[NUM_TEST_THREADS];
 void *my_retvals[NUM_TEST_THREADS];
@@ -24,7 +23,7 @@ void *my_retvals[NUM_TEST_THREADS];
 __thread int my_id;
 void *yield_thread(void* arg)
 {      
-       for (int i = 0; i < 10; i++) {
+       for (int i = 0; i < 100; i++) {
                printf_safe("[A] pthread %d on vcore %d\n", pthread_self(), vcore_id());
                pthread_yield();
                printf_safe("[A] pthread %d returned from yield on vcore %d\n",
@@ -39,6 +38,15 @@ void *hello_thread(void* arg)
        return (void*)(pthread_self());
 }
 
+//int main(int argc, char** argv) 
+//{
+//     void *retval1 = 0;
+//     printf_safe("[A] About to create thread 1\n");
+//     pthread_create(&t1, NULL, &yield_thread, NULL);
+//     printf_safe("[A] About to join on thread 1\n");
+//     pthread_join(t1, &retval1);
+//}
+
 int main(int argc, char** argv) 
 {
        void *retval1 = 0;
@@ -60,21 +68,18 @@ int main(int argc, char** argv)
        pthread_join(t2, &retval2);
        printf_safe("[A] Successfully joined on thread 2 (retval: %p)\n", retval2);
        printf_safe("[A] About to join on thread 3\n");
-       pthread_join(t3, NULL);
+       pthread_join(t3, &retval3);
        printf_safe("[A] Successfully joined on thread 3 (retval: %p)\n", retval3);
 
        /* create and join on hellos */
-       while (1) {
-               for (int i = 1; i < NUM_TEST_THREADS; i++) {
-                       printf_safe("[A] About to create thread %d\n", i);
-                       pthread_create(&my_threads[i], NULL, &hello_thread, NULL);
-               }
-               for (int i = 1; i < NUM_TEST_THREADS; i++) {
-                       printf_safe("[A] About to join on thread %d\n", i);
-                       pthread_join(my_threads[i], &my_retvals[i]);
-                       printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
-                                   my_retvals[i]);
-               }
-               break;
+       for (int i = 1; i < NUM_TEST_THREADS; i++) {
+               printf_safe("[A] About to create thread %d\n", i);
+               pthread_create(&my_threads[i], NULL, &yield_thread, NULL);
+       }
+       for (int i = 1; i < NUM_TEST_THREADS; i++) {
+               printf_safe("[A] About to join on thread %d\n", i);
+               pthread_join(my_threads[i], &my_retvals[i]);
+               printf_safe("[A] Successfully joined on thread %d (retval: %p)\n", i,
+                           my_retvals[i]);
        }
 } 
index efbbc74..44d58dd 100644 (file)
@@ -1,7 +1,7 @@
 TARGET_ARCH ?= i686
 CFLAGS = -O2 -static -std=gnu99 -fomit-frame-pointer -DOPTIMIZE=2 -DNO_TIMING
 LIBNAME = c3po
-SUBDIRS = util stack coro threads #aio 
+SUBDIRS = util stack threads #aio 
 V ?= @
 
 GCCPREFIX ?= $(TARGET_ARCH)-ros-
@@ -13,11 +13,10 @@ OBJDIR := $(SRCDIR)obj
 INCDIR = $(SRCDIR)include
 UTILDIR = $(SRCDIR)util
 STACKDIR = $(SRCDIR)stack
-CORODIR = $(SRCDIR)coro
 THREADSDIR = $(SRCDIR)threads
 AIODIR = $(SRCDIR)aio
 
-INCS = -I$(INCDIR) -I$(UTILDIR) -I$(THREADSDIR) -I$(STACKDIR) -I$(CORODIR) -I.
+INCS = -I$(INCDIR) -I$(UTILDIR) -I$(THREADSDIR) -I$(STACKDIR) -I.
 FINALLIB = $(OBJDIR)/lib$(LIBNAME).a
 
 uc = $(shell echo $(1) | tr a-z A-Z)
diff --git a/user/c3po/coro/Makefrag b/user/c3po/coro/Makefrag
deleted file mode 100644 (file)
index 02bbff2..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-CORO_NAME    := coro
-CORO_UCNAME  := $(call uc, $(CORO_NAME))
-CORO_CFLAGS  := $(CFLAGS)
-CORO_HEADERS := $(wildcard $(CORODIR)/*.h)
-CORO_CFILES  := $(wildcard $(CORODIR)/*.c)
-CORO_OBJDIR  := $(OBJDIR)/$(CORO_NAME)
-CORO_OBJS    := $(patsubst %.c, %.o, $(CORO_CFILES))
-CORO_OBJS    := $(foreach x, $(CORO_OBJS), $(CORO_OBJDIR)/$(call filename,$(x)))
-
-LIBCORO = $(CORO_OBJDIR)/lib$(CORO_NAME).a
-
-$(CORO_NAME)-clean:
-       @echo + clean [$(LIBUCNAME) $(CORO_UCNAME)]
-       $(V)rm -rf $(CORO_OBJS) $(LIBCORO)
-       $(V)rm -rf $(CORO_OBJDIR)
-
-$(LIBCORO): $(CORO_OBJS)
-       @echo + ar [$(LIBUCNAME) $(CORO_UCNAME)] $@
-       $(V)$(AR) rc $@ $^
-
-$(CORO_OBJDIR)/%.o: $(CORODIR)/%.c $(CORO_HEADERS)
-       @echo + cc [$(LIBUCNAME) $(CORO_UCNAME)] $<
-       @mkdir -p $(@D)
-       $(V)$(CC) $(CORO_CFLAGS) $(INCS) -o $@ -c $<
-
diff --git a/user/c3po/coro/coro.c b/user/c3po/coro/coro.c
deleted file mode 100644 (file)
index ef2ea2b..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-/*
-    Coroutine implementation for x86/linux, gcc-2.7.2
-
-    Copyright 1999 by E. Toernig <froese@gmx.de>
-*/
-
-#include <unistd.h>
-#include <stdarg.h>
-#include <sys/mman.h>
-#include <execinfo.h>
-#include "coro.h"
-
-#if !defined(i386)
-#error For x86-CPUs only
-#endif
-
-#if !defined(__GNUC__)
-#warning May break without gcc.  Be careful.
-#endif
-
-/* asm-name for identifier x */
-#if 1
-#define _(x) #x
-#else
-#define _(x) "_"#x
-#endif
-
-struct coroutine co_main[1] = { { NULL, NULL, NULL, NULL, NULL, 0 } };
-struct coroutine *co_current = co_main;
-
-
-#define fatal(msg) \
-    for (;;)                                           \
-    {                                                  \
-         int fee1dead = write(2, "coro: " msg "\r\n", sizeof(msg)+7);  \
-         *(unsigned int *)0 = fee1dead; \
-    }
-
-
-
-/*
-    Create new coroutine.
-    'func' is the entry point
-    'stack' is the start of the coroutines stack.  if 0, one is allocated.
-    'size' is the size of the stack
-*/
-
-
-static void wrap(void *data) __attribute__((noreturn,regparm(1)));
-
-static void __attribute__((noreturn,regparm(1))) 
-wrap(void *data) /* arg in %eax! */
-{
-    co_current->resumeto = co_current->caller;
-
-    for (;;)
-       data = co_resume(co_current->func(data));
-}
-
-struct coroutine *
-co_create(void *func, void *stack, int size)
-{
-    struct coroutine *co;
-    int to_free = 0;
-
-    if (size < 128)
-       return 0;
-
-    if (stack == 0)
-    {
-       size += 4096-1;
-       size &= ~(4096-1);
-       stack = mmap(0, size, PROT_READ|PROT_WRITE,
-                             MAP_PRIVATE|MAP_ANON, -1, 0);
-       if (stack == (void*)-1)
-           return 0;
-
-       to_free = size;
-    }
-    co = stack + size;
-    co = (struct coroutine*)((unsigned long)co & ~3);
-    co -= 1;
-
-    co->sp = co;
-    co->caller = 0;
-    co->resumeto = 0;
-    co->user = 0;
-    co->func = func;
-    co->to_free = to_free;
-
-    co->sp = ((void**)co->sp)-1; *(void**)co->sp = wrap;  // return addr (here: start addr)
-    co->sp = ((void**)co->sp)-1; *(void**)co->sp = 0;     // ebp
-    co->sp = ((void**)co->sp)-1; *(void**)co->sp = 0;     // ebx
-    co->sp = ((void**)co->sp)-1; *(void**)co->sp = 0;     // esi
-    co->sp = ((void**)co->sp)-1; *(void**)co->sp = 0;     // edi
-    return co;
-}
-
-
-
-/*
-    delete a coroutine.
-*/
-
-void
-co_delete(struct coroutine *co)
-{
-    if (co == co_current)
-       fatal("coroutine deletes itself");
-
-    if (co->to_free)
-       munmap((void *)co + sizeof(*co) - co->to_free, co->to_free);
-}
-
-
-
-/*
-    delete self and switch to 'new_co' passing 'data'
-*/
-
-static void *helper_args[2];
-
-static void
-del_helper(void **args)
-{
-    for (;;)
-    {
-       if (args != helper_args)
-           fatal("resume to deleted coroutine");
-       co_delete(co_current->caller);
-       args = co_call(args[0], args[1]);
-    }
-}
-
-void
-co_exit_to(struct coroutine *new_co, void *data)
-{
-    static struct coroutine *helper = 0;
-    static char stk[512]; // enough for a kernel call and a signal handler
-
-    // FIXME: multi-cpu race on use of helper!!  Solve by creating an array of helpers for each CPU
-    helper_args[0] = new_co;
-    helper_args[1] = data;
-
-    if (helper == 0)
-       helper = co_create(del_helper, stk, sizeof(stk));
-
-    // we must leave this coroutine.  so call the helper.
-    co_call(helper, helper_args);
-    fatal("stale coroutine called");
-}
-
-void
-co_exit(void *data)
-{
-    co_exit_to(co_current->resumeto, data);
-}
-
-
-
-/*
-    Call other coroutine.
-    'new_co' is the coroutine to switch to
-    'data' is passed to the new coroutine
-*/
-
-//void *co_call(struct coroutine *new_co, void *data) { magic }
-asm(   ".text"                         );
-asm(   ".align 16"                     );
-asm(   ".globl "_(co_call)             );
-asm(   ".type "_(co_call)",@function"  );
-asm(   _(co_call)":"                   );
-
-asm(   "pushl %ebp"                    );      // save reg-vars/framepointer
-asm(   "pushl %ebx"                    );
-asm(   "pushl %esi"                    );
-asm(   "pushl %edi"                    );
-
-asm(   "movl "_(co_current)",%eax"     );      // get old co
-asm(   "movl %esp,(%eax)"              );      // save sp of old co
-
-asm(   "movl 20(%esp),%ebx"            );      // get new co (arg1)
-asm(   "movl %ebx,"_(co_current)       );      // set as current
-asm(   "movl %eax,4(%ebx)"             );      // save caller
-asm(   "movl 24(%esp),%eax"            );      // get data (arg2)
-asm(   "movl (%ebx),%esp"              );      // restore sp of new co
-
-asm(   "popl %edi"                     );      // restore reg-vars/frameptr
-asm(   "popl %esi"                     );
-asm(   "popl %ebx"                     );
-asm(   "popl %ebp"                     );
-asm(   "ret"                           );      // return to new coro
-
-asm(   "1:"                            );
-asm(   ".size "_(co_call)",1b-"_(co_call));
-
-
-void *_co_esp, *_co_ebp, **_co_array;   // temp save place for co_backtrace
-static int _co_size, _co_rv;
-
-// we do this by temporarily switch to the coroutine and call backtrace()
-int co_backtrace(struct coroutine *cc, void **array, int size)
-{
-  (void) _co_esp; // avoid unused var warning
-  (void) _co_ebp;
-
-  if (cc == co_current)
-    return backtrace(array, size);
-      
-  // save arguments to global variable so we do not need to use the stack
-  // when calling backtrace()
-  _co_array = array;
-  _co_size = size;
-
-  asm(  "movl %ebp, "_(_co_ebp)      );   // save %ebp
-  asm(  "movl %esp, "_(_co_esp)      );   // save %esp
-  asm(  "movl (%%eax), %%esp" 
-                 : /* no output */ 
-                 : "a" (cc)         );   // set %esp to the co-routine
-  asm(  "movl %esp, %ebp"            );
-  asm(  "add  $12, %ebp"              );   // this is where co_call() saved the previous $ebp!
-  
-  _co_rv = backtrace(_co_array, _co_size);
-
-  asm(  "movl "_(_co_esp)", %esp"  );   // restore %esp
-  asm(  "movl "_(_co_ebp)", %ebp"  );   // restore %ebp
-
-  return _co_rv;
-}
-
-
-void *
-co_resume(void *data)
-{
-    data = co_call(co_current->resumeto, data);
-    co_current->resumeto = co_current->caller;
-    return data;
-}
-
-
diff --git a/user/c3po/coro/coro.h b/user/c3po/coro/coro.h
deleted file mode 100644 (file)
index 1c23597..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef CORO_H
-#define CORO_H
-
-struct coroutine
-{
-    void *sp;                  /* saved stack pointer while coro is inactive */
-    struct coroutine *caller;  /* PUBLIC who has called this coroutine */
-    struct coroutine *resumeto;        /* PUBLIC who to resume to */
-    void *user;                        /* PUBLIC user data.  for whatever you want. */
-
-    void *(*func)(void *);     /* coroutines main function */
-    int to_free;               /* how much memory to free on co_delete */
-};
-
-extern struct coroutine *co_current;   /* currently active coroutine */
-extern struct coroutine co_main[];     /* automagically generated main coro. */
-
-struct coroutine *co_create(void *func, void *stack, int stacksize);
-void *co_call(struct coroutine *co, void *data);
-void *co_resume(void *data);
-void co_delete(struct coroutine *co);
-void co_exit_to(struct coroutine *co, void *data) __attribute__((noreturn));
-void co_exit(void *data) __attribute__((noreturn));
-
-/* Store up to SIZE return addresses of the stack state of coroutine CC and return 
- * the number of values stored - zf */
-int co_backtrace(struct coroutine *cc, void **array, int size);
-
-#endif
diff --git a/user/c3po/threads/events.c b/user/c3po/threads/events.c
deleted file mode 100644 (file)
index e69de29..0000000
index 5ddb893..068e6c1 100644 (file)
@@ -7,6 +7,7 @@
 #include "threadlib.h"
 
 #include "util.h"
+#include <assert.h>
 #include <error.h>
 #include <stdlib.h>
 #include <sys/time.h>
index 8464080..761e2df 100644 (file)
@@ -6,12 +6,13 @@
 
 #include "threadlib_internal.h"
 #include "util.h"
+#include <stdio.h>
 
 #ifndef DEBUG_sched_global_rr_c
 #undef debug
 #define debug(...)
-#undef tdebug
-#define tdebug(...)
+//#undef tdebug
+//#define tdebug(...)
 #endif
 
 
index dafe4ec..b9e9228 100644 (file)
@@ -1,20 +1,31 @@
-#include "coro.h"
+// ROS Specific headers
+#include <vcore.h>
+#include <mcs.h>
+#include <ros/syscall.h>
+
+// Capriccio Specific headers
+#include "ucontext.h"
 #include "threadlib_internal.h"
 #include "util.h"
 #include "config.h"
 #include "blocking_graph.h"
 #include "stacklink.h"
 
+// Glibc Specific headers
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
 #include <execinfo.h>
 #include <sys/time.h>
 #include <unistd.h>
-#include <ros/syscall.h>
+#include <syscall.h>
 #include <sys/syscall.h>
 
-// comment out, to enable debugging in this file
+/******************************************************************************/
+/******************************* Configuration ********************************/
+/******************************************************************************/
+
+// Comment out, to enable debugging in this file
 #ifndef DEBUG_threadlib_c
 #undef debug
 #define debug(...)
 #define tdebug(...)
 #endif
 
-// FIXME: this doesn't work properly yet in all cases at program exit.
-// The performance does seem slightly better, however.
-//#define NO_SCHEDULER_THREAD 1
-
-
-// sanity check THREAD_KEY_MAX and size of key_data_count
-//#if THREAD_KEY_MAX >> (sizeof(thread_t.key_data_count)-1) != 1
-//#error not enough space in thread_t.key_data_count
-//#endif
+// Chose the size of the scheduler threads stack in the case of using
+// NIO vs. AIO
+#ifdef USE_NIO
+#define SCHEDULER_STACK_SIZE 1024*128
+#else
+#define SCHEDULER_STACK_SIZE 1024
+#endif
 
+/******************************************************************************/
+/***************************** Global Variables *******************************/
+/******************************************************************************/
 
 // The PID of the main process.  This is useful to prevent errors when
 // forked children call exit_func().
-//
-// FIXME: unfortunately, this still doesn't seem to work, as AIO gets
-// hosed if a forked child exits before the main thread.  This may be
-// a bug w/ AIO, however.
-static pid_t capriccio_main_pid;
-
-// flag to indicate whether or not to override syscalls.  We don't
-// override durring initialization, in order to avoid loops w/
-// libraries such as perfctr that must do IO.  We also don't override
-// syscalls after SIG_ABRT, so we can get proper core dumps.
-int cap_override_rw = 1;
+static pid_t main_pid;
 
-// flag so the signal stuff knows that the current thread is running in the scheduler
-int in_scheduler = 0;
-
-void **start_node_addrs = NULL;
-int *start_node_stacks = NULL;
+// Variable used to hold the time when the program first started executing
+static unsigned long long start_usec;
 
-#ifdef USE_NIO
-#define SCHEDULER_STACK_SIZE 1024*128
-#else
-#define SCHEDULER_STACK_SIZE 1024
-#endif
+// Variables used to store the amount of time spent in different parts of 
+// the application code: the scheduler, main thread, all other threads
+static struct {
+  cap_timer_t scheduler;
+  cap_timer_t main;
+  cap_timer_t app;
+} timers;
 
+// Pointer to the main thread
 static thread_t* main_thread=NULL;
-#ifndef NO_SCHEDULER_THREAD
-thread_t* scheduler_thread=NULL;
-#endif
-thread_t* current_thread=NULL;
-static int current_thread_exited = 0;
-
-// a list of all threads, used by sig_handler()
-pointer_list_t *threadlist = NULL;
-
-static int num_daemon_threads = 0;
-static int num_suspended_threads = 0;
-int num_runnable_threads = 0;
-static int num_zombie_threads = 0;
-#if OPTIMIZE < 1
-#define sanity_check_threadcounts() {\
-   assert(num_daemon_threads >= 0); \
-   assert(num_suspended_threads >= 0); \
-   assert(num_runnable_threads >= 0); \
-   assert(num_zombie_threads >= 0); \
-   assert(num_runnable_threads + num_suspended_threads + num_zombie_threads == pl_size(threadlist)); \
-}
-#define sanity_check_io_stats() {\
-   assert(sockio_stats.requests == sockio_stats.active + sockio_stats.completions + sockio_stats.errors); \
-   assert(diskio_stats.requests == diskio_stats.active + diskio_stats.completions + diskio_stats.errors); \
-}
-#else
-#define sanity_check_threadcounts()
-#define sanity_check_io_stats()
-#endif
 
-// modular scheduling functions
+// Lock used to restrict access to all thread lists, thread state changes,
+// and the count of threads in different states
+static mcs_lock_t thread_lock = MCS_LOCK_INIT;
+
+// A list of all threads in the system (i.e. spawned, but not exited yet)
+static pointer_list_t *threadlist = NULL;
+
+// Variable used to maintain global counts of how 
+// many threads are in a given state (used only for bookkeeping)
+static struct {
+  int running;
+  int runnable;
+  int suspended;
+  int detached;
+  int zombie;
+  int total;
+} num_threads = {0, 0, 0, 0};
+
+// Set of global flags
+static struct {
+  // Flag to indicate whether or not to override syscalls.  We don't
+  // override during initialization, in order to avoid loops w/
+  // libraries such as perfctr that must do IO.  We also don't override
+  // syscalls after SIG_ABRT, so we can get proper core dumps.
+  // Only really necessary for linux, as ROS does all IO asyncronously.
+  volatile unsigned int override_syscalls: 1;
+
+  // Flags regarding the state of the main_thread
+  volatile unsigned int main_exited: 1;
+  volatile unsigned int exit_func_done: 1;
+} gflags = {1, 0, 0};
+
+// Set of thread local flags (only useful in scheduler or vcore context)
+static __thread struct {
+  // Flag indicating whether we are currently running scheduler code 
+  // on the core or not
+  int in_scheduler: 1;
+} lflags = {0};
+
+// Function pointers for plugging in modular scheduling algorithms
+// Run queues are maintained locally in each algorithm
 static void (*sched_init)(void); 
 static void (*sched_add_thread)(thread_t *t); 
 static thread_t* (*sched_next_thread)(void); 
 
-// flags regarding the state of main_thread
-int exit_whole_program = 0;
-static int exit_func_done = 0;
-static int main_exited = 0;
+// Sleep queue, containing any sleeping threads
+static pointer_list_t *sleepq = NULL;
+
+// Variables used to regulate sleep times of sleeping threads
+static struct {
+  // When is the sleep time calculated from
+  unsigned long long last_check;   
+  // Wall clock time of the wake time of the first sleeping thread
+  unsigned long long first_wake;
+  // Length of the whole sleep queue, in microseconds
+  unsigned long long max;    
+} sleep_times = {0, 0, 0};
+
+// Function pointer to the io polling function being used
+// Only really makes sense in Linux, as ROS uses all async IO and
+// event queues for IO
+static void (*io_polling_func)(long long usecs);
+
+// Variables set by CIL when executing using linked stacks
+void **start_node_addrs = NULL;
+int *start_node_stacks = NULL;
 
+/******************************************************************************/
+/************************** Function Declarations *****************************/
+/******************************************************************************/
 
-// sleep queue, points to thread_t that's sleeping
-static pointer_list_t *sleepq = NULL;
-static unsigned long long last_check_time = 0;       // when is the sleep time calculated from
-static unsigned long long max_sleep_time=0;          // length of the whole sleep queue, in microseconds
-static unsigned long long first_wake_usecs=0;        // wall clock time of the wake time of the first sleeping thread
+// Vcore functions
+inline static bool __query_vcore_request();
+inline static bool __query_vcore_yield();
 
-inline static void free_thread( thread_t *t );
-inline static void sleepq_check();
+// Sleep queue functions
 inline static void sleepq_add_thread(thread_t *t, unsigned long long timeout);
 inline static void sleepq_remove_thread(thread_t *t);
+inline static void sleepq_check_wakeup();
 
+// Thread related functions
+void run_next_thread();
+inline static void __thread_resume(thread_t *t);
+inline static void __thread_make_runnable(thread_t *t);
+inline static void __free_thread_prep(thread_t *t);
+inline static void free_thread(thread_t *t);
 
-/**
- * set the IO polling function.  Used by the aio routines.  Shouldn't
- * be used elsewhere.
- **/
-// FIXME: it's ugly to break the namespaces up like this, but it is
-// still nice to be able to test the threads package independant of
-// the IO overriding stuff.
-static void (*io_polling_func)(long long usecs);
+// Helper Functions 
+static void exit_func(void);
+static void pick_scheduler();
+static int get_stack_size_kb_log2(void *func);
 
-void set_io_polling_func(void (*func)(long long))
-{
-  assert( !io_polling_func );
-  io_polling_func = func;
-}
+/******************************************************************************/
+/********************************** Macros ************************************/
+/******************************************************************************/
 
-unsigned long long start_usec;
+// Sanity check THREAD_KEY_MAX and size of key_data_count
+//#if THREAD_KEY_MAX >> (sizeof(thread_t.key_data_count)-1) != 1
+//#error not enough space in thread_t.key_data_count
+//#endif
 
-static cap_timer_t scheduler_timer;
-static cap_timer_t main_timer;
-static cap_timer_t app_timer;
+/******************************************************************************/
+/*************************** Function Definitions *****************************/
+/******************************************************************************/
 
 /**
- * Main scheduling loop
+ * This will be called as part of the initialization sequence
  **/
-static void* do_scheduler(void *arg)
+void main_thread_init() 
 {
-  static cpu_tick_t next_poll=0, next_overload_check=0, next_info_dump=0, next_graph_stats=0, now=0;
-  static int pollcount=1000;
+  tdebug("Enter\n");
   static int init_done = 0;
+  if(init_done) 
+    return;
+  init_done = 1;
 
-  (void) arg;  // suppress GCC "unused parameter" warning
-
-  in_scheduler = 1;
-
-  // make sure we start out by saving edge stats for a while
-  if( !init_done ) {
-    init_done = 1;
-    if (conf_no_statcollect) 
-      bg_save_stats = 0;
-    else
-      bg_save_stats = 1;
-    
-    GET_REAL_CPU_TICKS( now );
-    next_graph_stats = now + 1 * ticks_per_second;
-    
-    start_timer(&scheduler_timer);
-  }
-
-  while( 1 ) {
-
-    //current_thread = scheduler_thread;
-    sanity_check_threadcounts();
-    sanity_check_io_stats();
-
-    // wake up threads that have timeouts
-    sleepq_check(0);   
-    sanity_check_threadcounts();
-
-    // break out if there are only daemon threads
-    if(unlikely (num_suspended_threads == 0  &&  num_runnable_threads == num_daemon_threads)) {
-      // dump the blocking graph
-      if( exit_func_done && conf_dump_blocking_graph ) {
-        tdebug("dumping blocking graph from do_scheduler()\n");
-        dump_blocking_graph(); 
-      }
-        
-      // go back to mainthread, which should now be in exit_func()
-      current_thread = main_thread;
-      in_scheduler = 0;
-      co_call(main_thread->coro, NULL);
-      in_scheduler = 1;
-
-      if( unlikely(current_thread_exited) ) {     // free memory from deleted threads
-        current_thread_exited=0;
-        if (current_thread != main_thread) // main_thread is needed for whole program exit
-          free_thread( current_thread );
-      }
-        
-      return NULL;
-    }
-
-
-    // cheesy way of handling things with timing requirements
-    {
-      GET_REAL_CPU_TICKS( now );
-        
-      // toggle stats collection
-      if( conf_no_statcollect == 0 && next_graph_stats < now ) {
-        bg_save_stats = 1 - bg_save_stats;
-
-        if( bg_save_stats ) { 
-          // record stats for 100 ms
-          next_graph_stats = now + 100 * ticks_per_millisecond;
-            
-          // update the stats epoch, to allow proper handling of the first data items
-          bg_stats_epoch++;
-        }            
-        else {
-          // avoid stats for 2000 ms
-          next_graph_stats = now + 2000 * ticks_per_millisecond;
-        }
-        //output(" *********************** graph stats %s\n", bg_save_stats ? "ON" : "OFF" );
-      }
-        
-      // resource utalization
-      //if( unlikely (next_overload_check < now) ) {
-      //  check_overload( now );
-      //  next_overload_check = now + OVERLOAD_CHECK_INTERVAL;
-      //}
-
-      // poll
-      if( likely( (int)io_polling_func) ) {
-        if( num_runnable_threads==0  ||  --pollcount <= 0  ||  next_poll < now ) {
-          //if( num_runnable_threads==0 ) {
-          // poll
-          long long timeout = 0;
-
-          if( num_runnable_threads==0 ) {
-            if (first_wake_usecs == 0) {
-              timeout = -1;
-            } else {
-              // there are threads in the sleep queue
-              // so poll for i/o till at most that time
-              unsigned long long now;
-              now = current_usecs();
-             tdebug ("first_wake: %lld, now: %lld\n", first_wake_usecs, now);
-              if (first_wake_usecs > now)
-                timeout = first_wake_usecs - now;
-            }
-          }
-
-          stop_timer(&scheduler_timer);
-          //if( timeout != -1 )  output("timeout is not zero\n");
-          io_polling_func( timeout ); // allow blocking
-          start_timer(&scheduler_timer);
-          sanity_check_threadcounts();
-
-
-#ifndef USE_NIO
-          // sleep for a bit, if there was nothing to do
-          // FIXME: let the IO functions block instead??
-          if( num_runnable_threads == 0 ) {
-            syscall(SYS_yield);
-          }
-#endif
-
-          // vary the poll rate depending on the workload
-#if 0
-          if( num_runnable_threads < 5 ) {
-            next_poll = now + (10*ticks_per_millisecond);
-            pollcount = 1000;
-          } else if( num_runnable_threads < 10 ) {
-            next_poll = now + (50*ticks_per_millisecond);
-            pollcount = 2000;
-          } else {
-            next_poll = now + (100*ticks_per_millisecond);
-            pollcount = 3000;
-          }
-#else
-          next_poll = now + (ticks_per_millisecond << 13);
-         pollcount = 10000;
-
-#endif
-        }
-      }
-
-      // debug stats
-      if( 0 && next_info_dump < now ) {
-        dump_debug_info();
-        next_info_dump = now + 5 * ticks_per_second;
-      }
+  // Make sure the clock init is already done, so we don't wind up w/
+  // a dependancy loop b/w perfctr and the rest of the code.
+//  init_cycle_clock();
+//  init_debug();
 
-    }
+  // Initialize and Register all timers
+//  init_timer(&timers.main);
+//  init_timer(&timers.scheduler);
+//  init_timer(&timers.app);
+//  register_timer("total", &timers.main);
+//  register_timer("sheduler", &timers.scheduler);
+//  register_timer("app", &timers.app);
 
-    // get the head of the run list
-    current_thread = sched_next_thread();
+  // Start the main timer
+//  start_timer(&timers.main);
 
-    // scheduler gave an invlid even though there are runnable
-    // threads.  This indicates that every runnable thead is likely to
-    // require use of an overloaded resource. 
-    if( !valid_thread(current_thread) ) {
-      pollcount = 0;
-      continue;
-    }
+  // Init the scheduler function pointers
+  pick_scheduler();
 
-    // barf, if the returned thread is still on the sleep queue
-    assert( current_thread->sleep == -1 );
+  // Init the scheduler code
+  sched_init();
 
-    tdebug("running TID %d (%s)\n", current_thread->tid, current_thread->name ? current_thread->name : "no name");
+  // Create the main thread
+  main_thread = malloc(sizeof(thread_t));  
+  assert(main_thread);
+  bzero(main_thread, sizeof(thread_t));
+  main_thread->context = create_context(main_thread, NULL, NULL);
+  main_thread->name = "main_thread";
+  main_thread->initial_arg = NULL;
+  main_thread->initial_func = NULL;
+  main_thread->tid = 0;   // fixed value
+  main_thread->joinable = 0;
+  main_thread->sleep = -1;
 
-    sanity_check_threadcounts();
+  // Create global thread list
+  threadlist = new_pointer_list("thread_list");
 
+  // Create sleep queue
+  sleepq = new_pointer_list("sleep_queue");
+  sleep_times.max = 0;
+  sleep_times.last_check = 0;
+  sleep_times.first_wake = 0;
 
-    // call thread
-    stop_timer(&scheduler_timer);
-    start_timer(&app_timer);
-    in_scheduler = 0;
-    co_call(current_thread->coro, NULL);
-    in_scheduler = 1;
-    stop_timer(&app_timer);
-    start_timer(&scheduler_timer);
+  // Add main thread to the global list of threads
+  mcs_lock_lock(&thread_lock);
+  pl_add_tail(threadlist, main_thread);
+  num_threads.total++;
+  // Update number of running threads
+  main_thread->state = RUNNING;
+  num_threads.running++;
+  num_threads.detached++;
+  mcs_lock_unlock(&thread_lock);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+  
+  // intialize blocking graph functions
+//  init_blocking_graph();
+
+//  // Set stats for the main thread
+//  {
+//    bg_dummy_node->num_here++;
+//    current_thread->curr_stats.node = bg_dummy_node;
+//    current_thread->curr_stats.files = 0;
+//    current_thread->curr_stats.sockets = 0;
+//    current_thread->curr_stats.heap = 0;
+//    bg_set_current_stats( &current_thread->curr_stats );
+//
+//    current_thread->prev_stats = current_thread->curr_stats;
+//  }
 
-    if( unlikely(current_thread_exited) ) {     // free memory from deleted threads
-      current_thread_exited=0;
-      if (current_thread != main_thread) // main_thread is needed for whole program exit
-        free_thread( current_thread );
-    }
+  // Setup custom exit function called by glibc when exit() called
+  // Don't exit when main exits - wait for threads to die
+  atexit(exit_func);
 
-#ifdef NO_SCHEDULER_THREAD
-    return NULL;
-#endif
-  }
+  // Mark the time the program starts running
+//  start_usec = current_usecs();
 
-  return NULL;
+  // Things are all set up, so now turn on the syscall overrides
+  // Only really required by the linux port.
+//  gflags.override_syscalls = 1;
+  tdebug("Exit\n");
 }
 
-
-
-static int get_stack_size_kb_log2(void *func)
+/**
+ * Function to select and launch the next thread with the selected scheduler.
+ * Only called from within vcore context.  Keep this in mind when reasoning
+ * about how thread local variables, etc. are used.
+ **/
+void run_next_thread()
 {
-  int result = conf_new_stack_kb_log2;
-  if (start_node_addrs != NULL) {
-    int i = 0;
-    while (start_node_addrs[i] != NULL && start_node_addrs[i] != func) {
-      i++;
+  tdebug("Enter\n");
+  // Make sure we start out by saving edge stats
+//  static int init_done = 0;
+//  static cpu_tick_t next_info_dump = 0, next_graph_stats = 0, now = 0;
+//  if( !init_done ) {
+//    init_done = 1;
+//    if (conf_no_statcollect) 
+//      bg_save_stats = 0;
+//    else
+//      bg_save_stats = 1;
+//    
+//    GET_REAL_CPU_TICKS( now );
+//    next_graph_stats = now + 1 * ticks_per_second;
+//    
+//    start_timer(&timers.scheduler);
+//  }
+//  // Cheesy way of handling things with timing requirements
+//  {
+//    GET_REAL_CPU_TICKS( now );
+//      
+//    // toggle stats collection
+//    if( conf_no_statcollect == 0 && next_graph_stats < now ) {
+//      bg_save_stats = 1 - bg_save_stats;
+// 
+//      if( bg_save_stats ) { 
+//        // record stats for 100 ms
+//        next_graph_stats = now + 100 * ticks_per_millisecond;
+//          
+//        // update the stats epoch, to allow proper handling of the first data items
+//        bg_stats_epoch++;
+//      }            
+//      else {
+//        // avoid stats for 2000 ms
+//        next_graph_stats = now + 2000 * ticks_per_millisecond;
+//      }
+//      //output(" *********************** graph stats %s\n", bg_save_stats ? "ON" : "OFF" );
+//    }
+//      
+//    // Poll for I/O
+//    static cpu_tick_t next_poll = 0;
+//    static int pollcount = 1000;
+//    if( likely( (int)io_polling_func) ) {
+//      if( num_threads.runnable == 0  ||  --pollcount <= 0  ||  next_poll < now ) {
+//        long long timeout = 0;
+// 
+//        if( num_threads.runnable == 0 ) {
+//          if (sleep_times.first_wake == 0) {
+//            timeout = -1;
+//          } else {
+//            // there are threads in the sleep queue
+//            // so poll for i/o till at most that time
+//            unsigned long long now;
+//            now = current_usecs();
+//            tdebug ("first_wake: %lld, now: %lld\n", sleep_times.first_wake, now);
+//            if (sleep_times.first_wake > now)
+//              timeout = sleep_times.first_wake - now;
+//          }
+//        }
+// 
+//        stop_timer(&timers.scheduler);
+//        //if( timeout != -1 )  output("timeout is not zero\n");
+//        io_polling_func( timeout ); // allow blocking
+//        start_timer(&timers.scheduler);
+// 
+//        next_poll = now + (ticks_per_millisecond << 13);
+//        pollcount = 10000;
+//      }
+//    }
+//
+//    // Gather debug stats
+//    if( 0 && next_info_dump < now ) {
+//      dump_debug_info();
+//      next_info_dump = now + 5 * ticks_per_second;
+//    }
+//  }
+
+  // Wake up threads that are asleep who's timeouts have expired
+  sleepq_check_wakeup(FALSE);   
+  // Keep trying to get a thread off of the scheduler queue
+  thread_t *t; 
+  while(1) {
+    mcs_lock_lock(&thread_lock);
+       // Check to see if we are in the processes of exiting the entire program.
+       // If we are, then go ahead and yield this vcore. We are dying, afterall..
+       if(gflags.exit_func_done) {
+      bool yieldcore = __query_vcore_yield();
+      mcs_lock_unlock(&thread_lock);
+      if(yieldcore) vcore_yield();
     }
-    if (start_node_addrs[i] == func) {
-      result = start_node_stacks[i];
-    } else {
-      fatal("Couldn't find stack size for thread entry point %p\n", func);
+               
+    // Otherwise, grab a thread from the scheduler queue 
+    t = sched_next_thread();
+
+       // If there aren't any, if there aren't any, go ahead and yield the core
+       // back to the kernel.  This can only really happen when there are only
+       // running and suspended threads in the system, but no runnable ones. When
+       // a suspended thread is woken up, it will try and request a new vcore from
+       // the system if appropriate.
+    if(t == NULL) {
+      bool yieldcore = __query_vcore_yield();
+      mcs_lock_unlock(&thread_lock);
+      if(yieldcore) vcore_yield();
+    }
+       // Otherwise, if the thread is in the ZOMBIE state, then it must have been
+       // detached and added back to the queue for the scheduler to reap.  
+    // Reap it now, then go and grab the next thread.
+    else if(t->state == ZOMBIE) {
+      __free_thread_prep(t);
+      mcs_lock_unlock(&thread_lock);
+      free_thread(t);
     }
+    // Otherwise, we've found a thread to run, so continue.
+    else
+      break;
   }
-  return result;
+  // Update the num_threads variables and the thread state
+  num_threads.runnable--;
+  num_threads.running++;
+  t->state = RUNNING;
+  mcs_lock_unlock(&thread_lock);
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+  tdebug("About to run TID %d (%s)\n", t->tid, t->name ? t->name : "no name");
+  // Run the thread
+//  stop_timer(&timers.scheduler);
+//  start_timer(&timers.app);
+  tdebug("Exit\n");
+  assert(!current_thread);
+  restore_context(t->context);
 }
 
-
 /**
  * Wrapper function for new threads.  This allows us to clean up
  * correctly if a thread exits without calling thread_exit().
  **/
-static void* new_thread_wrapper(void *arg)
+static void* __attribute__((noreturn)) new_thread_wrapper(void *arg)
 {
-  void *ret;
-  (void) arg;
-
+  tdebug("Enter\n");
   // set up initial stats
-  current_thread->curr_stats.files = 0;
-  current_thread->curr_stats.sockets = 0;
-  current_thread->curr_stats.heap = 0;
-  bg_set_current_stats( &current_thread->curr_stats );
-  current_thread->prev_stats = current_thread->curr_stats;
-
-  // set up stack limit for new thread
-  stack_bottom = current_thread->stack_bottom;
-  stack_fingerprint = current_thread->stack_fingerprint;
+//  current_thread->curr_stats.files = 0;
+//  current_thread->curr_stats.sockets = 0;
+//  current_thread->curr_stats.heap = 0;
+//  bg_set_current_stats( &current_thread->curr_stats );
+//  current_thread->prev_stats = current_thread->curr_stats;
+//
+//  // set up stack limit for new thread
+//  stack_bottom = current_thread->stack_bottom;
+//  stack_fingerprint = current_thread->stack_fingerprint;
 
   // start the thread
   tdebug("Initial arg = %p\n", current_thread->initial_arg);
-  ret = current_thread->initial_func(current_thread->initial_arg);
+  void *ret = current_thread->initial_func(current_thread->initial_arg);
   
   // call thread_exit() to do the cleanup
   thread_exit(ret);
-  
-  return NULL;
+  assert(0);
+}
+
+inline static bool __query_vcore_request()
+{
+  return FALSE;
+  //return TRUE;
+  //return (num_vcores() < 2);
+  //return ((num_threads.total-num_threads.zombie) > num_vcores());
+  //return ((num_threads.total-num_threads.zombie) > num_vcores());
+}
+
+inline static bool __query_vcore_yield()
+{
+  return FALSE;
+  //return TRUE;
 }
 
+/* Create a new thread and add it to the scheduler queue */
 static thread_t* new_thread(char *name, void* (*func)(void *), void *arg, thread_attr_t attr)
 {
+  tdebug("Enter\n");
   static unsigned max_tid = 1;
   thread_t *t = malloc( sizeof(thread_t) );
-  int stack_size_kb_log2 = get_stack_size_kb_log2(func);
-  void *stack = stack_get_chunk( stack_size_kb_log2 );
+  int stack_size_kb_log2 = 10;//get_stack_size_kb_log2(func);
   int stack_size = 1 << (stack_size_kb_log2 + 10);
+  void *stack = malloc(stack_size);//stack_get_chunk( stack_size_kb_log2 );
 
   if( !t || !stack ) {
     if (t) free(t);
-    if (stack) stack_return_chunk(stack_size_kb_log2, stack);
+    if (stack) free(stack);//stack_return_chunk(stack_size_kb_log2, stack);
+    printf("Uh Oh!\n");
     return NULL;
   }
 
   bzero(t, sizeof(thread_t));
-
-  t->coro = co_create(new_thread_wrapper, stack - stack_size, stack_size);
+  t->context = create_context(t, new_thread_wrapper, stack+stack_size);
   t->stack = stack;
   t->stack_size_kb_log2 = stack_size_kb_log2;
-  t->stack_bottom = stack - stack_size;
+  t->stack_bottom = stack;
   t->stack_fingerprint = 0;
   t->name = (name ? name : "noname"); 
   t->initial_func = func;
@@ -407,101 +480,121 @@ static thread_t* new_thread(char *name, void* (*func)(void *), void *arg, thread
   t->tid = max_tid++;
   t->sleep = -1;
 
+  // Make sure the thread has a valid node before we add it to the scheduling list
+//  bg_dummy_node->num_here++;
+//  t->curr_stats.node = bg_dummy_node;
+
+  mcs_lock_lock(&thread_lock);
+  // Up the count of detached threads if this thread should be detached
   if( attr ) {
     t->joinable = attr->joinable;
-    t->daemon = attr->daemon;
-    if(t->daemon)
-      num_daemon_threads++;
+    if(!t->joinable) {
+      num_threads.detached++;
+    }
   }
-
-  // FIXME: somehow track the parent thread, for stats creation?
-
-  // make sure the thread has a valid node before we add it to the scheduling list
-  bg_dummy_node->num_here++;
-  t->curr_stats.node = bg_dummy_node;
-
+  // Add the thread to the global list of all threads
   pl_add_tail(threadlist, t);
-
-  num_runnable_threads++;
+  num_threads.total++;
+  // Add the thread to the scheduler to make it runnable
   sched_add_thread(t);
-  sanity_check_threadcounts();
-
+  t->state = RUNNABLE;
+  num_threads.runnable++;
+  bool requestcore = __query_vcore_request();
+  mcs_lock_unlock(&thread_lock);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+
+  /* Possibly request a new vcore.  In the best case, we have 1 core per thread
+   * that we launch.  If not, it never hurts to ask for another one.  The
+   * system will simply deny us and the scheduler will multiplex all threads on
+   * the available vcores. */
+  if(requestcore) vcore_request(1);
+
+  tdebug("Exit\n");
+  // Return the newly created thread.
   return t;
 }
 
-
 /**
  * Free the memory associated with the given thread.
+ * Needs to be protected by the thread_lock.
  **/
-inline static void free_thread( thread_t *t )
+inline static void __free_thread_prep(thread_t *t)
 {
-  static int iter = -1;
-  iter++;
+  // Make sure we should actually be freeing this thread
+  assert(t->state == ZOMBIE);
+  // Make this zombie a ghost!
+  t->state = GHOST;
+  // Drop the count of zombie threads in the system
+  num_threads.zombie--;
+  // Remove this thread from the global list of all threads
   pl_remove_pointer(threadlist, t);
+  num_threads.total--;
 
-  assert(t->state == ZOMBIE);
-  t->state = GHOST;  // just for good measure
-  num_zombie_threads--;
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+}
 
+/**
+ * Actually free the memory associated with a thread.  Should only be called
+ * after first calling __free_thread_prep while holding the thread_lock.  Make
+ * sure to NOT call this function while holding the thread_lock though.
+ **/
+inline static void free_thread(thread_t *t)
+{
+  // Free the thread memory
   if( t != main_thread ) {
-    co_delete( t->coro );
-    stack_return_chunk( t->stack_size_kb_log2, t->stack );
-    free( t );
+    free(t->stack);//stack_return_chunk(t->stack_size_kb_log2, t->stack);
   }
+  destroy_context(t->context);
+  free(t);
 }
 
-/*
-
-void exit(int code) {
-       fprintf (stderr, "exit called!");
-       exit_whole_program = 1;
-    syscall(SYS_exit, code);
-faint: goto faint;
-}
-*/
-
-#ifndef NO_ATEXIT
 /**
- * give control back to the scheduler after main() exits.  This allows
+ * Give control back to the scheduler after main() exits.  This allows
  * remaining threads to continue running.
- * FIXME: we don't know whether user explicit calls exit() or main() normally returns
- * in the previous case, we should exit immediately, while in the later, we should 
+ * FIXME: we don't know whether user explicitly calls exit() or main() normally returns
+ * in the previous case, we should exit immediately, while in the latter, we should 
  * join other threads.
  * Overriding exit() does not work because normal returning from
  * main() also calls exit().
  **/
 static void exit_func(void)
 {
-  // don't do anything if we're in a forked child process
-  if( getpid() != capriccio_main_pid )
+  tdebug("Enter\n");
+  // Don't do anything if we're in a forked child process
+  if(current_thread != main_thread)
     return;
 
-  exit_func_done = 1;
-  main_exited = 1;
-  if( !exit_whole_program )
+  tdebug("current=%s, gflags.main_exited=%d\n", 
+          current_thread?current_thread->name : "NULL", gflags.main_exited);
+
+  gflags.main_exited = TRUE;
+//  if( !gflags.exit_whole_program )
        // this will block until all other threads finish
     thread_exit(NULL);
 
-  // dump the blocking graph before we exit
-  if( conf_dump_blocking_graph ) {
-    tdebug("dumping blocking graph from exit_func()\n");
-    dump_blocking_graph(); 
-  }
-
-  // FIXME: make sure to kill cloned children
-
-  if( conf_dump_timing_info ) {
-    if( main_timer.running )   stop_timer(&main_timer);
-    if( scheduler_timer.running )   stop_timer(&scheduler_timer);
-    if( app_timer.running )   stop_timer(&app_timer);
-    print_timers();
-  }
+//  // dump the blocking graph before we exit
+//  if( conf_dump_blocking_graph ) {
+//    tdebug("dumping blocking graph from exit_func()\n");
+//    dump_blocking_graph(); 
+//  }
+//
+//  // FIXME: make sure to kill cloned children
+//
+//  if( conf_dump_timing_info ) {
+//    if( timers.main.running )   stop_timer(&timers.main);
+//    if( timers.scheduler.running )   stop_timer(&timers.scheduler);
+//    if( timers.app.running )   stop_timer(&timers.app);
+//    print_timers();
+//  }
+  tdebug("Exit\n");
 }
-#endif
-
-static char *THREAD_STATES[] = {"RUNNABLE", "SUSPENDED", "ZOMBIE", "GHOST"};
-
 
+static char *THREAD_STATES[] = {"RUNNING", "RUNNABLE", "SUSPENDED", "ZOMBIE", "GHOST"};
 
 // dump status to stderr 
 void dump_debug_info()
@@ -510,8 +603,8 @@ void dump_debug_info()
   output("Current thread %d  (%s)\n", 
          current_thread ? (int)thread_tid(current_thread) : -1,
          (current_thread && current_thread->name) ? current_thread->name : "noname");
-  output("threads:    %d runnable    %d suspended    %d daemon\n", 
-         num_runnable_threads, num_suspended_threads, num_daemon_threads);
+  output("threads:    %d runnable    %d suspended    %d detached\n", 
+         num_threads.runnable, num_threads.suspended, num_threads.detached);
 
   print_resource_stats();
 
@@ -552,34 +645,7 @@ void dump_thread_state()
            t->curr_stats.heap / 1024,
            //(long)-1, // FIXME: add total stack numbers after incorporating Jeremy's code
            t->joinable ? "joinable  " : "",
-           t->daemon ? "daemon  " : "",
            t->sleep > 0 ? (sprintf(sleepstr,"sleep=%lld  ",t->sleep), sleepstr) : "");
-
-    if( conf_show_thread_stacks ) {
-      count = co_backtrace(t->coro, bt, 100);
-      if (count == 100)
-        output("WARN: only output first 100 stack frames.\n");
-      
-#if (0)
-      {
-        void **frame;
-        frame = bt;
-        while( count-- )
-          output("    %p\n",*(frame++));
-      }
-#else
-      {
-        // NOTE: backtrace_symbols_fd causes a loop w/ our IO functions.
-        char **p = backtrace_symbols(bt, count);
-        for (i = 0; i < count; i++)
-          output("   %s\n", *(p+i));
-        free(p);
-      }
-#endif
-      
-      output("\n");
-    }
-      
     e = ll_view_next(threadlist, e);
   }
 
@@ -628,127 +694,175 @@ static void pick_scheduler()
 }
 
 /**
- * perform necessary management to yield the current thread
- * if suspended == TRUE && timeout != 0 -> the thread is added 
- * to the sleep queue and later waken up when the clock times out
- * returns FALSE if time-out actually happens, TRUE if waken up
- * by other threads, INTERRUPTED if interrupted by a signal
+ * Perform necessary management to yield the current thread
+ * if suspend == TRUE && timeout != 0 -> the thread is added 
+ * to the sleep queue and later woken up when the clock times out.
+ * Returns FALSE if time-out actually happens, TRUE if woken up
+ * by other threads, INTERRUPTED if interrupted by a signal.
  **/
-static int thread_yield_internal(int suspended, unsigned long long timeout)
+static int __thread_yield(int suspend, unsigned long long timeout)
 {
-// now we use a per-thread errno stored in thread_t
-   int savederrno;
-  int rv = OK;
-
-  tdebug("current_thread=%p\n",current_thread);
-
+  tdebug("Enter\n");
+  // Now we use a per-thread errno stored in thread_t
+  int savederrno;
   savederrno = errno;
 
-  // decide what to do with the thread
-  if( !suspended ) // just add it to the runlist
-    sched_add_thread( current_thread );
-  else if( timeout ) // add to the sleep list
-    sleepq_add_thread( current_thread, timeout);
-
-  {
-#ifdef SHOW_EDGE_TIMES
-    cpu_tick_t start, end, rstart, rend;
-    GET_CPU_TICKS(start);
-    GET_REAL_CPU_TICKS(rstart);
-#endif
-
-    // figure out the current node in the graph
-    if( !conf_no_stacktrace )
-      bg_backtrace_set_node();
-    // FIXME: fake out what cil would do...  current_thread->curr_stats.node = bg_dummy_node;
-
-    // we should already have been told the node by CIL or directly by the programmer
-    assert( current_thread->curr_stats.node != NULL );
-    
-    // update node counts
-    current_thread->prev_stats.node->num_here--;
-    current_thread->curr_stats.node->num_here++;
-    
-    // update the blocking graph info
-    if( bg_save_stats )
-      bg_update_stats();
-  
-#ifdef SHOW_EDGE_TIMES
-    GET_CPU_TICKS(end);
-    GET_REAL_CPU_TICKS(rend);
-    {
-      thread_stats_t *curr = &current_thread->curr_stats;
-      thread_stats_t *prev = &current_thread->prev_stats;
-      output(" %3d -> %-3d     %7lld ticks  (%lld ms)   %7lld rticks (%lld ms)    ", 
-             prev->node->node_num,  curr->node->node_num, 
-             curr->cpu_ticks - prev->cpu_ticks,
-             (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond,
-# ifdef USE_PERFCTR
-             curr->real_ticks - prev->real_ticks,
-             (curr->real_ticks - prev->real_ticks) / ticks_per_millisecond
-# else
-             curr->cpu_ticks - prev->cpu_ticks,
-             (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond
-# endif
-             );
-
-      output("update bg node %d:   %lld   (%lld ms)   real: %lld (%lld ms)\n", 
-             current_thread->curr_stats.node->node_num, 
-             (end-start), (end-start)/ticks_per_millisecond, 
-             (rend-rstart), (rend-rstart)/ticks_per_millisecond);
-    }
-#endif
-  }
-
-  // squirrel away the stack limit for next time
-  current_thread->stack_bottom = stack_bottom;
-  current_thread->stack_fingerprint = stack_fingerprint;
-
-  // switch to the scheduler thread
-#ifdef NO_SCHEDULER_THREAD
-  do_scheduler(NULL);
-#else
-  co_call(scheduler_thread->coro, NULL);
-#endif
-  
-  // set up stack limit for new thread
-  stack_bottom = current_thread->stack_bottom;
-  stack_fingerprint = current_thread->stack_fingerprint;
+  tdebug("current_thread=%p\n",current_thread);
 
-  // rotate the stats
-  if( bg_save_stats ) {
-    current_thread->prev_stats = current_thread->curr_stats;
-    
-    // update thread time, to skip time asleep
-    GET_CPU_TICKS( current_thread->prev_stats.cpu_ticks );
-    current_thread->prev_stats.cpu_ticks -= ticks_diff;  // FIXME: subtract out time to do debug output
-#ifdef USE_PERFCTR
-    GET_REAL_CPU_TICKS( current_thread->prev_stats.real_ticks );
-    current_thread->prev_stats.real_ticks -= ticks_rdiff;  // FIXME: subtract out time to do debug output
-#endif    
-  } else {
-    current_thread->prev_stats.node = current_thread->curr_stats.node;
+//  {
+//#ifdef SHOW_EDGE_TIMES
+//    cpu_tick_t start, end, rstart, rend;
+//    GET_CPU_TICKS(start);
+//    GET_REAL_CPU_TICKS(rstart);
+//#endif
+//
+//    // Figure out the current node in the graph
+//    if( !conf_no_stacktrace )
+//      bg_backtrace_set_node();
+//    // FIXME: fake out what cil would do...  current_thread->curr_stats.node = bg_dummy_node;
+//
+//    // We should already have been told the node by CIL or directly by the programmer
+//    assert( current_thread->curr_stats.node != NULL );
+//    
+//    // Update node counts
+//    current_thread->prev_stats.node->num_here--;
+//    current_thread->curr_stats.node->num_here++;
+//    
+//    // Update the blocking graph info
+//    if( bg_save_stats )
+//      bg_update_stats();
+//  
+//#ifdef SHOW_EDGE_TIMES
+//    GET_CPU_TICKS(end);
+//    GET_REAL_CPU_TICKS(rend);
+//    {
+//      thread_stats_t *curr = &current_thread->curr_stats;
+//      thread_stats_t *prev = &current_thread->prev_stats;
+//      output(" %3d -> %-3d     %7lld ticks  (%lld ms)   %7lld rticks (%lld ms)    ", 
+//             prev->node->node_num,  curr->node->node_num, 
+//             curr->cpu_ticks - prev->cpu_ticks,
+//             (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond,
+//# ifdef USE_PERFCTR
+//             curr->real_ticks - prev->real_ticks,
+//             (curr->real_ticks - prev->real_ticks) / ticks_per_millisecond
+//# else
+//             curr->cpu_ticks - prev->cpu_ticks,
+//             (curr->cpu_ticks - prev->cpu_ticks) / ticks_per_millisecond
+//# endif
+//             );
+//
+//      output("update bg node %d:   %lld   (%lld ms)   real: %lld (%lld ms)\n", 
+//             current_thread->curr_stats.node->node_num, 
+//             (end-start), (end-start)/ticks_per_millisecond, 
+//             (rend-rstart), (rend-rstart)/ticks_per_millisecond);
+//    }
+//#endif
+//  }
+
+  // Decide what to do with the thread
+  mcs_lock_lock(&thread_lock);
+  // Drop the count of running threads
+  num_threads.running--;
+  // If we should suspend it, do so for the specified timeout
+  if(suspend) { 
+    current_thread->state = SUSPENDED;
+    num_threads.suspended++;
+       // Add the thread to the sleep queue if a timeout was given
+    // If no timeout was given, that means we should sleep forever
+       // or until some other thread wakes us up (i.e. on a join)      
+    if(timeout)
+      sleepq_add_thread(current_thread, timeout);
   }
-  
-  // check whether time-out happens
-  if (suspended && timeout && current_thread->timeout) {
-    rv = TIMEDOUT;
-    current_thread->timeout = 0;
+  else {
+    current_thread->state = RUNNABLE;
+    num_threads.runnable++;
+    sched_add_thread(current_thread);
   }
-
-  // check for and process pending signals
-  if ( likely(!current_thread->sig_waiting) ) {
-       //if (sig_process_pending())
-               rv = INTERRUPTED;
-  } else {
-       // if sig_waiting is 1, sigwait() itself will handle the remaining      
-       rv = INTERRUPTED;
+  mcs_lock_unlock(&thread_lock);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+
+//  // squirrel away the stack limit for next time
+//  current_thread->stack_bottom = stack_bottom;
+//  current_thread->stack_fingerprint = stack_fingerprint;
+
+  // Save the context of the currently running thread.
+  // When it is restored it will start up again right here
+  save_context(current_thread->context);
+
+  // We only want to call switch_to_vcore() when running through
+  // this function at the time of the yield() call.  Since our 
+  // context is restored right here though, we need a way to jump around
+  // the call to switch_to_vcore() once the thread is woken back up.
+  volatile bool yielding = TRUE;
+  if (yielding) {
+    yielding = FALSE; /* for when it starts back up */
+    // Switch back to vcore context to run the scheduler
+    switch_to_vcore();
   }
+  // Thread context restored...
+  
+//  // Set up stack limit for new thread
+//  stack_bottom = current_thread->stack_bottom;
+//  stack_fingerprint = current_thread->stack_fingerprint;
+//
+//  // rotate the stats
+//  if( bg_save_stats ) {
+//    current_thread->prev_stats = current_thread->curr_stats;
+//    
+//    // update thread time, to skip time asleep
+//    GET_CPU_TICKS( current_thread->prev_stats.cpu_ticks );
+//    current_thread->prev_stats.cpu_ticks -= ticks_diff;  // FIXME: subtract out time to do debug output
+//#ifdef USE_PERFCTR
+//    GET_REAL_CPU_TICKS( current_thread->prev_stats.real_ticks );
+//    current_thread->prev_stats.real_ticks -= ticks_rdiff;  // FIXME: subtract out time to do debug output
+//#endif    
+//  } else {
+//    current_thread->prev_stats.node = current_thread->curr_stats.node;
+//  }
+//  
+//  // Check whether time-out happened already or not
+  int rv = OK;
+//  if (suspend && timeout && current_thread->timeout) {
+//    rv = TIMEDOUT;
+//    current_thread->timeout = 0;
+//  }
+//
+//  // Check for and process pending signals
+//  if ( likely(!current_thread->sig_waiting) ) {
+//    if (sig_process_pending())
+//             rv = INTERRUPTED;
+//  } else {
+//       // If sig_waiting is 1, sigwait() itself will handle the remaining    
+//       rv = INTERRUPTED;
+//  }
   
   errno = savederrno;
+  tdebug("Exit\n");
   return rv;
 }
 
+void thread_yield()
+{
+  CAP_SET_SYSCALL();
+  __thread_yield(FALSE,0);
+  CAP_CLEAR_SYSCALL();
+}
+
+int sched_yield(void)
+{
+  thread_yield();
+  return 0;
+}
+strong_alias(sched_yield,__sched_yield);
+
+// Timeout == 0 means infinite time
+int thread_suspend_self(unsigned long long timeout)
+{
+  return __thread_yield(TRUE, timeout);
+}
 
 //////////////////////////////////////////////////////////////////////
 // 
@@ -756,116 +870,6 @@ static int thread_yield_internal(int suspended, unsigned long long timeout)
 // 
 //////////////////////////////////////////////////////////////////////
 
-/**
- * This will be called automatically, either by routines here, or by
- * the AIO routines
- **/
-static void thread_init()  __attribute__ ((constructor));
-static void thread_init() 
-{
-  static int init_done = 0;
-
-  capriccio_main_pid = getpid();
-
-  // read config info from the environemtn
-  read_config();
-
-  //assert(0);
-  if(init_done) 
-    return;
-  init_done = 1;
-
-  // make sure the clock init is already done, so we don't wind up w/
-  // a dependancy loop b/w perfctr and the rest of the code.
-  init_cycle_clock();
-  init_debug();
-
-  // start main timer
-  init_timer(&main_timer);
-  register_timer("total", &main_timer);
-  start_timer(&main_timer);
-
-  init_timer(&scheduler_timer);
-  register_timer("sheduler", &scheduler_timer);
-
-  init_timer(&app_timer);
-  register_timer("app", &app_timer);
-
-  // init scheduler function pointers
-  pick_scheduler();
-
-  // init the scheduler code
-  sched_init();
-
-  // create the main thread
-  main_thread = malloc(sizeof(thread_t));  
-  assert(main_thread);
-  bzero(main_thread, sizeof(thread_t));
-  main_thread->name = "main_thread";
-  main_thread->coro = co_main;
-  main_thread->initial_arg = NULL;
-  main_thread->initial_func = NULL;
-  main_thread->tid = 0;   // fixed value
-  main_thread->sleep = -1;
-  current_thread = main_thread;
-
-  // create the scheduler thread
-#ifndef NO_SCHEDULER_THREAD
-  scheduler_thread = (thread_t*) malloc( sizeof(thread_t) ); 
-  assert(scheduler_thread);
-  bzero(scheduler_thread, sizeof(thread_t));
-  scheduler_thread->name = "scheduler";
-  scheduler_thread->coro = co_create(do_scheduler, 0, SCHEDULER_STACK_SIZE);
-  scheduler_thread->tid = -1;
-#endif
-
-  // don't exit when main exits - wait for threads to die
-#ifndef NO_ATEXIT
-  atexit(exit_func);
-#endif
-
-  // intialize blocking graph functions
-  init_blocking_graph();
-
-  // set stats for the main thread
-  {
-    bg_dummy_node->num_here++;
-    current_thread->curr_stats.node = bg_dummy_node;
-    current_thread->curr_stats.files = 0;
-    current_thread->curr_stats.sockets = 0;
-    current_thread->curr_stats.heap = 0;
-    bg_set_current_stats( &current_thread->curr_stats );
-
-    current_thread->prev_stats = current_thread->curr_stats;
-  }
-
-  // create thread list
-  threadlist = new_pointer_list("thread_list");
-  // add main thread to the list
-  pl_add_tail(threadlist, main_thread);
-  num_runnable_threads++;
-  
-  // create sleep queue
-  sleepq = new_pointer_list("sleep_queue");
-  max_sleep_time = 0;
-  last_check_time = 0;
-  first_wake_usecs = 0;
-  start_usec = current_usecs();
-  
-  // make sure the scheduler runs.  NOTE: this is actually very
-  // important, as it prevents a degenerate case in which the main
-  // thread exits before the scheduler is ever called.  This will
-  // actually cause a core dump, b/c the current_thead_exited flag
-  // will be set, and incorrectly flag the first user thread for
-  // deletion, rather than the main thread.
-  thread_yield_internal(FALSE, 0);
-
-  // things are all set up, so now turn on the syscall overrides
-  cap_override_rw = 1;
-}
-
-
 inline thread_t *thread_spawn_with_attr(char *name, void* (*func)(void *), 
                                  void *arg, thread_attr_t attr)
 {
@@ -877,145 +881,213 @@ inline thread_t *thread_spawn(char *name, void* (*func)(void *), void *arg)
   return new_thread(name, func, arg, NULL);
 }
 
-
-void thread_yield()
-{
-  CAP_SET_SYSCALL();
-  thread_yield_internal( FALSE, 0 );
-  CAP_CLEAR_SYSCALL();
+static inline bool time_to_die() {
+return ((gflags.main_exited == TRUE) &&
+        ((num_threads.total-num_threads.zombie) == num_threads.detached)
+       );
 }
 
 void thread_exit(void *ret)
 {
+  tdebug("Enter\n");
   thread_t *t = current_thread;
 
-  sanity_check_threadcounts();
-  tdebug("current=%s\n", current_thread?current_thread->name : "NULL");
+  //printf("current=%s, gflags.main_exited=%d\n", 
+  //        current_thread?current_thread->name : "NULL", gflags.main_exited);
 
-  if (current_thread == main_thread && main_exited == 0) {
-       // the case when the user calls thread_exit() in main thread is complicated
+  if (current_thread == main_thread && gflags.main_exited == FALSE) {
+       // The case when the user calls thread_exit() in main thread is complicated
        // we cannot simply terminate the main thread, because we need that stack to terminate the
        // whole program normally.  so we call exit() to make the c runtime help us get the stack
        // context where we can just return to terminate the whole program
        // this will call exit_func() and in turn call thread_exit() again
-    main_exited = 1;
-       exit (0);               
+    gflags.main_exited = TRUE;
+       exit(0);                
   }
 
-  // note the thread exit in the blocking graph
-  t->curr_stats.node = bg_exit_node;
-  current_thread->prev_stats.node->num_here--;
-  current_thread->curr_stats.node->num_here++;
-  if( bg_save_stats ) {
-    bg_update_stats();
-  }
+//  // Note the thread exit in the blocking graph
+//  current_thread->curr_stats.node = bg_exit_node;
+//  current_thread->prev_stats.node->num_here--;
+//  current_thread->curr_stats.node->num_here++;
+//  if( bg_save_stats ) {
+//    bg_update_stats();
+//  }
     
-  // update thread counts
-  num_runnable_threads--;
-  if( t->daemon ) num_daemon_threads--;
+  // If we are the main thread...
+  while(unlikely(t == main_thread)) {
+    // Check if we really can exit the program now.
+    // If so, end of program!
+    if(time_to_die()) {
+      //// Dump the blocking graph
+      //if( gflags.exit_func_done && conf_dump_blocking_graph ) {
+      //  tdebug("dumping blocking graph from run_next_thread()\n");
+      //  dump_blocking_graph(); 
+      //}
+
+      // Return back to glibc and exit the program!
+         // First set a global flag so no other vcores try to pull new threads off
+         // of any lists (these lists are about to be deallocated...)
+      mcs_lock_lock(&thread_lock);
+      gflags.exit_func_done = TRUE;
+      mcs_lock_unlock(&thread_lock);
 
+      printf("Dying with %d vcores\n", num_vcores());
+      printf("Program exiting normally!\n");
+      return;
+    }
+    // Otherwise, suspend ourselves to be woken up when it is time to die
+    else {
+      // Suspend myself
+      thread_suspend_self(0);
+    }
+  }
+  // Otherwise...
+  // Update thread counts and resume blocked threads
+  mcs_lock_lock(&thread_lock);
+  num_threads.running--;
+  num_threads.zombie++;
   t->state = ZOMBIE;
-  num_zombie_threads++;
-
-  // deallocate the TCB
-  // keep the thread, if the thread is Joinable, and we want the return value for something
-  if ( !( t->joinable ) ) {
-    // tell the scheduler thread to delete the current one
-    current_thread_exited = 1;
-  } else {
+
+  // Check if it's time to die now. If it is, wakeup the main thread so we can
+  // exit the program
+  if(unlikely(time_to_die()))
+      __thread_resume(main_thread);
+
+  // Otherwise, if the thread is joinable, resume the thread that joined on it.
+  // If no one has joined on it yet, we have alreadu set its thread state to
+  // ZOMBIE so that the thread that eventually tries to join on it can see
+  // this, and free it.
+  else if(likely(t->joinable)) {
     t->ret = ret;
-    if (t->join_thread)
-      thread_resume(t->join_thread);
+    if (t->join_thread) {
+      __thread_resume(t->join_thread);
+    }
   }
 
-  sanity_check_threadcounts();
-
-  // squirrel away the stack limit--not that we'll need it again
-  current_thread->stack_bottom = stack_bottom;
-  current_thread->stack_fingerprint = stack_fingerprint;
+  // Otherwise, update the count of detached threads and put the thread back on
+  // the scheduler queue. The thread will be freed by the scheduler the next
+  // time it attempts to run.
+  else {
+    num_threads.detached--;
+    sched_add_thread(t);
+  }
 
-  // give control back to the scheduler
-#ifdef NO_SCHEDULER_THREAD
-  do_scheduler(NULL);
-#else
-  co_call(scheduler_thread->coro, NULL);
-#endif
+  // Check to see if we now have less threads than we have vcores.  If so,
+  // prepare to yield the current core back to the system.
+  bool yieldcore = __query_vcore_yield();
+  mcs_lock_unlock(&thread_lock);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+
+  /* If we were told to yield the vcore, do it! */
+  if(yieldcore)
+    vcore_yield();
+         
+  /* Otherwise switch back to vcore context to schedule the next thread. */
+  switch_to_vcore();
+  assert(0);
 }
 
 int thread_join(thread_t *t, void **ret)
 {
-  if (t == NULL)
+  tdebug("Enter\n");
+  // Return errors if the argument is bogus
+  if(t == NULL)
     return_errno(FALSE, EINVAL);
-  if ( !( t->joinable ) )
+  if(!t->joinable)
     return_errno(FALSE, EINVAL);
 
-  assert(t->state != GHOST);
-
   // A thread can be joined only once
-  if (t->join_thread)   
+  if(t->join_thread)   
     return_errno(FALSE, EACCES);   
-  t->join_thread = current_thread;
 
   // Wait for the thread to complete
-  tdebug( "**** thread state: %d\n" ,t->state);
-  if (t->state != ZOMBIE) {
+  tdebug( "thread state: %d\n" ,t->state);
+  mcs_lock_lock(&thread_lock);
+  if(t->state != ZOMBIE) {
+    t->join_thread = current_thread;
+    mcs_lock_unlock(&thread_lock);
        CAP_SET_SYSCALL();
     thread_suspend_self(0);
     CAP_CLEAR_SYSCALL();
+    mcs_lock_lock(&thread_lock);
   }
 
-  // clean up the dead thread
-  if (ret != NULL) 
+  // Set the return value
+  if(ret != NULL) 
     *ret = t->ret;
-  free_thread( t );
 
-  return TRUE;
-}
+  // Free the memory associated with the joined thread. 
+  __free_thread_prep(t);
+  mcs_lock_unlock(&thread_lock);
+  free_thread(t);
 
-// timeout == 0 means infinite time
-int thread_suspend_self(unsigned long long timeout)
-{
-  num_suspended_threads++;
-  num_runnable_threads--;
-  sanity_check_threadcounts();
-  current_thread->state = SUSPENDED;
-  return thread_yield_internal(TRUE, timeout);
+  tdebug("Exit\n");
+  return TRUE;
 }
 
-// only resume the thread internally
-// don't touch the timeout flag and the sleep queue
-static void _thread_resume(thread_t *t)
+// Only resume the thread internally
+// Don't touch the timeout flag and the sleep queue
+// Call to this needs to be protected by the thread_lock
+static void __thread_make_runnable(thread_t *t)
 {
+  tdebug("Enter\n");
   tdebug("t=%p\n",t);
   if (t->state != SUSPENDED)
     return;
-  num_suspended_threads--;
-  num_runnable_threads++;
-  sanity_check_threadcounts();
-  assert(t->state == SUSPENDED);
-  t->state = RUNNABLE;
 
+  assert(t->state == SUSPENDED);
   assert( t->sleep == -1 );
+  t->state = RUNNABLE;
+  num_threads.suspended--;
+  num_threads.runnable++;
   sched_add_thread(t);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
+
+  tdebug("Exit\n");
 }
 
-void thread_resume(thread_t *t)
+// Resume a sleeping thread
+// Call to this needs to be protected by the thread_lock
+static void __thread_resume(thread_t *t)
 {
-  // clear timer
+  // Remove the thread from the sleep queue
   if (t->sleep != -1)
     sleepq_remove_thread(t);
 
-  // make the thread runnable
-  _thread_resume(t);
+  // Make the thread runnable
+  __thread_make_runnable(t);
+}
+
+void thread_resume(thread_t *t)
+{
+  mcs_lock_lock(&thread_lock);
+  __thread_resume(t);
+  bool requestcore = __query_vcore_request();
+  mcs_lock_unlock(&thread_lock);
+
+  // Maybe request a new vcore if we are running low
+  if(requestcore) vcore_request(1);
 }
 
-void thread_set_daemon(thread_t *t)
+void thread_set_detached(thread_t *t)
 {
-  if( t->daemon )
+  if(!t->joinable)
     return;
   
-  t->daemon = 1;
-  num_daemon_threads++;
+  mcs_lock_lock(&thread_lock);
+  t->joinable = 0;
+  num_threads.detached++;
+  mcs_lock_unlock(&thread_lock);
+
+  tdebug("running: %d, runnable: %d, suspended: %d, detached: %d\n", 
+         num_threads.running, num_threads.runnable, 
+         num_threads.suspended, num_threads.detached);
 }
 
 inline char* thread_name(thread_t *t)
@@ -1025,12 +1097,10 @@ inline char* thread_name(thread_t *t)
 
 void thread_exit_program(int exitcode)
 {
-  exit_whole_program = 1;
   raise( SIGINT );
   syscall(SYS_proc_destroy, exitcode);
 }
 
-
 // Thread attribute handling
 thread_attr_t thread_attr_of(thread_t *t) {
   thread_attr_t attr = (thread_attr_t)malloc(sizeof(struct _thread_attr));
@@ -1214,50 +1284,12 @@ void thread_key_destroydata(thread_t *t)
     return;
 }
 
-// for finding the location of the current errno variable
-/*
-int __global_errno = 0;
-int *__errno_location (void)
-{
-       if (likely((int)current_thread))
-               return &current_thread->__errno;
-       else
-               return &__global_errno;
-}
-*/
-
 unsigned thread_tid(thread_t *t)
 {
   return t ? t->tid : 0xffffffff;
 }
 
-#if 1
-#define sleepq_sanity_check() \
-       assert ((max_sleep_time > 0 && sleepq->num_entries > 0) \
-               || (max_sleep_time == 0 && sleepq->num_entries == 0) )
-#else
-#define sleepq_sanity_check() \
-do { \
-  assert ((max_sleep_time > 0 && sleepq->num_entries > 0) \
-       | (max_sleep_time == 0 && sleepq->num_entries == 0) ); \
- { \
-  linked_list_entry_t *e; \
-  unsigned long long _total = 0; \
-  e = ll_view_head(sleepq);\
-  while (e) {\
-    thread_t *tt = (thread_t *)pl_get_pointer(e);\
-    assert( tt->sleep >= 0 );\
-    _total += tt->sleep;\
-    e = ll_view_next(sleepq, e);\
-  }\
-  assert( _total == max_sleep_time );\
- }\
-} while( 0 );
-#endif
-
-
-int print_sleep_queue(void) __attribute__((unused));
-int print_sleep_queue(void)
+int __attribute__((unused)) print_sleep_queue(void)
 {
   linked_list_entry_t *e; 
   unsigned long long _total = 0; 
@@ -1272,41 +1304,54 @@ int print_sleep_queue(void)
   return 1;
 }
 
-// check sleep queue to wake up all timed-out threads
-// sync == TRUE -> synchronize last_check_time
-static void sleepq_check(int sync)
+/**
+ * Put a thread to sleep for the specified timeout 
+ **/
+void thread_usleep(unsigned long long timeout)
 {
-  unsigned long long now;
-  long long interval;
-  linked_list_entry_t *e;
+  thread_suspend_self(timeout);
+}
 
-  if (!sync && max_sleep_time == 0) {  // shortcut to return
-    first_wake_usecs = 0;      // FIXME: don't write to it every time
+/**
+ * Check sleep queue to wake up all timed-out threads
+ * sync == TRUE -> force synchronization of last_check_time
+ **/
+static void sleepq_check_wakeup(int sync)
+{
+  // Shortcut to return if no threads sleeping
+  if (!sync && sleep_times.max == 0) {  
+    sleep_times.first_wake = 0;
     return;
   }
 
-  sleepq_sanity_check();
-
+  // Get interval since last check time and update 
+  // last check time to now
+  unsigned long long now;
+  long long interval;
   now = current_usecs();
-  if( now > last_check_time ) 
-    interval = now-last_check_time;
+  if( now > sleep_times.last_check ) 
+    interval = now-sleep_times.last_check;
   else 
     interval = 0;
-  last_check_time = now;
-
+  sleep_times.last_check = now;
 
-  // adjust max_sleep_time
-  if (max_sleep_time < (unsigned long long)interval)
-    max_sleep_time = 0;
+  // Adjust max_sleep_time based on the interval just computed
+  if (sleep_times.max < (unsigned long long)interval)
+    sleep_times.max = 0;
   else
-    max_sleep_time -= interval;
+    sleep_times.max -= interval;
   
+  // Walk through the sleepq and pull off and resume all threads
+  // whose remaining sleep time is less than the interval since 
+  // the last check. If it's greater, update the remaining sleep time
+  // and set first_wake to now + the new sleep time.
+  linked_list_entry_t *e;
   while (interval > 0 && (e = ll_view_head(sleepq))) {
     thread_t *t = (thread_t *)pl_get_pointer(e);
 
     if (t->sleep > interval) {
       t->sleep -= interval;
-      first_wake_usecs = now + t->sleep;
+      sleep_times.first_wake = now + t->sleep;
       break;
     }
 
@@ -1314,50 +1359,55 @@ static void sleepq_check(int sync)
     t->sleep = -1;
     t->timeout = 1;
 
-    //output("  %10llu: thread %d timeout\n", current_usecs(), t->tid);
-    
-    _thread_resume(t);    // this doesn't deal with sleep queue
+    mcs_lock_lock(&thread_lock);
     ll_free_entry(sleepq, ll_remove_head(sleepq));
+    __thread_make_runnable(t);
+    mcs_lock_unlock(&thread_lock);
   }
 
   if (ll_size(sleepq) == 0) {
      // the sleepq is empty again
-     first_wake_usecs = 0;
+     sleep_times.first_wake = 0;
   }
-
-  sleepq_sanity_check();
 }
 
-
-// set a timer on a thread that will wake the thread up after timeout
-// microseconds.  this is used to implement thread_suspend_self(timeout)
+/**
+ * Set a timer on a thread that will wake up after timeout
+ * microseconds.  This is used to implement thread_suspend_self(timeout)
+ **/
 static void sleepq_add_thread(thread_t *t, unsigned long long timeout)
 {
-  linked_list_entry_t *e;
-  long long total_time;
-  sleepq_check(1); // make sure: last_check_time == now
-
+  // Make sure the current thread doesn't already have a sleep time set
   assert(t->sleep == -1);
-  sleepq_sanity_check();
 
-  if (timeout >= max_sleep_time) {
-    // set first_wake_usecs if this is the first item
+  // No need to grab the sleepq_lock before making the following function
+  // call, as calls to this function should already be protected by it.
+  sleepq_check_wakeup(TRUE); // make sure: last_check_time == now
+  
+  // If the tieout is greater than the maximum sleep time of the 
+  // longest sleeping thread, update the maximum, set the sleep
+  // time of the thread (relative to all inserted before it), and
+  // update the max sleep time
+  if (timeout >= sleep_times.max) {
+    // Set sleep_times.first_wake if this is the first item
     if( pl_view_head(sleepq) == NULL )
-      first_wake_usecs = current_usecs() + timeout;
+      sleep_times.first_wake = current_usecs() + timeout;
 
-    // just append the thread to the end of sleep queue
+    // Just append the thread to the end of sleep queue
     pl_add_tail(sleepq, t);
-    t->sleep = timeout - max_sleep_time;
+    t->sleep = timeout - sleep_times.max;
     assert( t->sleep >= 0 );
-    max_sleep_time = timeout;
-    sleepq_sanity_check();
+    sleep_times.max = timeout;
     return;
   }
 
-  // let's find a place in the queue to insert the thread
-  // we go backwards
+  // Otherwise we need to find the proper place to insert the thread in
+  // the sleep queue, given its timeout length. 
+  // We search the list backwards.
+  linked_list_entry_t *e;
+  long long total_time;
   e = ll_view_tail(sleepq);
-  total_time = max_sleep_time;
+  total_time = sleep_times.max;
   while (e) {
     thread_t *tt = (thread_t *)pl_get_pointer(e);
     assert(tt->sleep >= 0);
@@ -1371,88 +1421,91 @@ static void sleepq_add_thread(thread_t *t, unsigned long long timeout)
       t->sleep = timeout - total_time;
       assert( t->sleep > 0 );
 
-      // set first_wake_usecs if this is the first item
+      // set sleep_times.first_wake if this is the first item
       if( total_time == 0 )
-        first_wake_usecs = current_usecs() + timeout;
+        sleep_times.first_wake = current_usecs() + timeout;
 
       // update the sleep time of the thread right after t
       tt->sleep -= t->sleep;
       assert( tt->sleep > 0 );
       break;
     }
-    
     e = ll_view_prev(sleepq, e);
   }
 
-  assert (e != NULL);   // we're sure to find such an e
-  sleepq_sanity_check();
-  
-  return;
+  // We're sure to find such an e
+  assert (e != NULL);
 }
 
-// remove the timer associated with the thread
+/**
+ * Remove a sleeping thread from the sleep queue before
+ * its timer expires.
+ **/
 inline static void sleepq_remove_thread(thread_t *t)
 {
-  linked_list_entry_t *e;
-
-  assert(t->sleep >= 0);  // the thread must be in the sleep queue
-  sleepq_sanity_check();
+  // The thread must be in the sleep queue
+  assert(t->sleep >= 0);  
   
-  // let's find the thread in the queue
+  // Let's find the thread in the queue
+  linked_list_entry_t *e;
   e = ll_view_head(sleepq);
   while (e) {
     thread_t *tt = (thread_t *)pl_get_pointer(e);
     if (tt == t) {
       linked_list_entry_t *nexte = ll_view_next(sleepq, e);
       if (nexte) {
-       // e is not the last thread in the queue
-       // we need to lengthen the time the next thread will sleep
-       thread_t *nextt = (thread_t *)pl_get_pointer(nexte);
-       nextt->sleep += t->sleep;
+           // e is not the last thread in the queue
+           // we need to lengthen the time the next thread will sleep
+           thread_t *nextt = (thread_t *)pl_get_pointer(nexte);
+           nextt->sleep += t->sleep;
       } else {
-       // e is the last thread, so we need to adjust max_sleep_time
-       max_sleep_time -= t->sleep;
+           // e is the last thread, so we need to adjust max_sleep_time
+           sleep_times.max -= t->sleep;
       }
       // remove t
       ll_remove_entry(sleepq, e);
       ll_free_entry(sleepq, e);
       t->sleep = -1;
-      assert (!t->timeout);    // if this fails, someone must ha
-                               // forgot to reset timeout some time ago
+      assert (!t->timeout);    // if this fails, someone must have
+                               // forgotten to reset timeout some time ago
       break;
     }
     e = ll_view_next(sleepq, e);
   }
-
   assert( t->sleep == -1);
   assert (e != NULL);   // we must find t in sleep queue
-  sleepq_sanity_check();
 }
 
-
-void thread_usleep(unsigned long long timeout)
+/**
+ * Set the IO polling function.  Used by the aio routines.  Shouldn't
+ * be used elsewhere.
+ **/
+void set_io_polling_func(void (*func)(long long))
 {
-  thread_suspend_self(timeout);
+  assert( !io_polling_func );
+  io_polling_func = func;
 }
 
 
-// NOTE: dynamic linking craps out w/o these.  There may be a better way.
-//static int capriccio_errno=0;
-//int* __errno_location()
-//{
-//  return &capriccio_errno;
-//}
-
-//extern pid_t __libc_fork();
-//pid_t fork() {
-//  return __libc_fork();
-//}
-//strong_alias(fork,__fork);
+/******************************************************************************/
+/****************************** Helper Functions ******************************/
+/******************************************************************************/
 
-
-int sched_yield(void)
+static int get_stack_size_kb_log2(void *func)
 {
-  thread_yield();
-  return 0;
+  int result = conf_new_stack_kb_log2;
+  if (start_node_addrs != NULL) {
+    int i = 0;
+    while (start_node_addrs[i] != NULL && start_node_addrs[i] != func) {
+      i++;
+    }
+    if (start_node_addrs[i] == func) {
+      result = start_node_stacks[i];
+    } else {
+      fatal("Couldn't find stack size for thread entry point %p\n", func);
+    }
+  }
+  return result;
 }
-strong_alias(sched_yield,__sched_yield);
+
+
index ae926d1..708b749 100644 (file)
@@ -4,6 +4,7 @@
 
 #include "pthread.h"
 #include "resource_stats.h"
+#include <stdbool.h>
 #include <time.h>
 #include <signal.h>
 
@@ -76,6 +77,8 @@ enum {
 
 typedef struct _thread_attr *thread_attr_t;
 
+extern void switch_to_vcore();
+extern void run_next_thread();
 void thread_exit(void *ret);
 void thread_exit_program(int exitcode);
 void thread_yield();
@@ -172,9 +175,6 @@ extern iostats_t diskio_stats;
   else            type##_stats.errors++; \
 }
 
-extern int cap_override_rw;
-
-
 extern const char *cap_current_syscall;  // used to inform the BG routines how they got there...
 #define CAP_SET_SYSCALL()   if(!cap_current_syscall) cap_current_syscall = __FUNCTION__
 #define CAP_CLEAR_SYSCALL() (cap_current_syscall = NULL)
index 256ab96..7b1c62f 100644 (file)
@@ -2,12 +2,11 @@
 #ifndef THREADLIB_INTERNAL_H
 #define THREADLIB_INTERNAL_H
 
-#include "pthread.h"
-#include "coro.h"
 #include "threadlib.h"
 #include "util.h"
 #include "blocking_graph.h"
 #include "signal.h"
+#include "ucontext.h"
 #ifdef USE_NIO
 #include <libaio.h>
 #endif
@@ -32,7 +31,8 @@ struct _thread_attr {
 
 struct thread_st {
   unsigned tid;   // thread id, mainly for readability of debug output
-  struct coroutine *coro;
+  struct thread_st *self; // pointer to itself 
+  struct ucontext *context;
   void *stack;
   void *stack_bottom;
   int stack_fingerprint;
@@ -40,14 +40,14 @@ struct thread_st {
 
   // misc. short fields, grouped to save space
   enum {
-    RUNNABLE=0,
+       RUNNING=0,
+    RUNNABLE,
     SUSPENDED,
-    ZOMBIE,        // not yet reaped, for joinable threads
-    GHOST          // already freed, no valid thread should be in this state
+    ZOMBIE,          // not yet reaped, for joinable threads
+    GHOST            // already freed, no valid thread should be in this state
   } state:3;
 
   unsigned int joinable:1;
-  unsigned int daemon:1;
   unsigned int key_data_count:8;  // big enough for THREAD_KEY_MAX
   unsigned int timeout:1;         // whether it is waken up because of timeout
   unsigned int sig_waiting:1;  // the thread is waiting for a signal (any not blocked in sig_mask). 
@@ -149,6 +149,4 @@ extern long long total_stack_in_use;
 // process all pending signals.  returns 1 is any actually handled, 0 otherwise
 extern int sig_process_pending();
 
-extern thread_t *scheduler_thread;
-
 #endif /* THREADLIB_INTERNAL_H */
diff --git a/user/c3po/threads/ucontext.c b/user/c3po/threads/ucontext.c
new file mode 100644 (file)
index 0000000..e2b6372
--- /dev/null
@@ -0,0 +1,98 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <threadlib_internal.h>
+#include <vcore.h>
+#include <ucontext.h>
+
+
+/* Maintain a static reference to the main threads tls region */
+static void *__main_tls_desc;
+
+struct ucontext* create_context(thread_t *t, void *entry_pt, void *stack_top)
+{
+       uint32_t vcoreid = vcore_id();
+
+       /* Allocate a new context struct */
+       struct ucontext *uc = malloc(sizeof(struct ucontext));
+       if(!uc) return NULL;
+
+       /* If we are the main thread, then current_thread has not been set yet, so
+        * we set the tls descriptor to the tls on the currently running core */
+       if(current_thread == NULL) {
+               /* Make sure this only executes once! */
+               static int done = FALSE;
+               assert(!done); done = TRUE;
+
+               /* Set up the tls regions for the main thred */
+               uc->tls_desc = get_tls_desc(vcoreid);
+               __main_tls_desc = uc->tls_desc;
+
+               /* And set the current thread in this tls region */
+               current_thread = t;
+       }
+    /* Otherwise, allocate a new tls region for this context */
+       else {
+               /* Allocate the tls, freeing the whole uc if it fails */
+       uc->tls_desc = allocate_tls();
+               if(!uc->tls_desc) {
+                       free(uc);
+                       return NULL;
+               }
+               /* Temporarily switch into the new the context's tls region to set the
+                * current_thread variable in that tls region */
+               void *temp_tls = get_tls_desc(vcoreid);
+               set_tls_desc(uc->tls_desc, vcoreid);
+               current_thread = t;
+               /* Then switch back to the origin one */
+               set_tls_desc(temp_tls, vcoreid);
+       }
+
+       /* For good measure, also save the thread associated with this context in
+        * the ucontext struct */
+       uc->thread = t;
+
+       /* Initialize the trapframe for this context */
+       init_user_tf(&uc->utf, (uint32_t)entry_pt, (uint32_t)stack_top);
+       return uc;
+}
+
+void save_context(struct ucontext *uc)
+{
+       /* Save the trapframe for this context */
+       save_ros_tf(&uc->utf);
+}
+
+void restore_context(struct ucontext *uc)
+{
+       uint32_t vcoreid = vcore_id();
+
+       /* Save the thread we are about to restore into current_thread
+        * in this vcores tls region */
+       current_thread = uc->thread;
+       /* Set the proper tls descriptor for the context we are restoring */
+    set_tls_desc(uc->tls_desc, vcoreid);
+       /* Pop the trapframe */
+       pop_ros_tf(&uc->utf, vcoreid);
+}
+
+void destroy_context(struct ucontext *uc)
+{
+    extern void _dl_deallocate_tls (void *tcb, bool dealloc_tcb) internal_function;
+
+       /* Make sure we have passed in Non-Null pointers for our ucontext / tls */
+    assert(uc);
+    assert(uc->tls_desc);
+
+       /* Dont' deallocate the main tls descriptor, glibc will do that */
+       if(uc->tls_desc != __main_tls_desc)
+       _dl_deallocate_tls(uc->tls_desc, TRUE);
+       free(uc);
+}
+
+void print_context(struct ucontext *uc)
+{
+       /* Just print the trapframe */
+       print_trapframe(&uc->utf);
+}
+
diff --git a/user/c3po/threads/ucontext.h b/user/c3po/threads/ucontext.h
new file mode 100644 (file)
index 0000000..f9f70cc
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef UCONTEXT_H
+#define UCONTEXT_H
+
+#include <stdint.h>
+#include <ros/arch/trapframe.h>
+#include <threadlib_internal.h>
+struct ucontext {
+       struct user_trapframe utf;
+    void *tls_desc;
+       thread_t *thread;
+}; 
+
+struct ucontext* create_context(thread_t *t, void *entry_pt, void *stack_top);
+void save_context(struct ucontext *uc);
+void restore_context(struct ucontext *uc);
+void destroy_context(struct ucontext *uc);
+void print_context(struct ucontext *uc);
+
+#endif
diff --git a/user/c3po/threads/vcore.c b/user/c3po/threads/vcore.c
new file mode 100644 (file)
index 0000000..8f58cfe
--- /dev/null
@@ -0,0 +1,202 @@
+#include <vcore.h>
+#include <mcs.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <parlib.h>
+#include <ros/event.h>
+#include <arch/atomic.h>
+#include <arch/arch.h>
+#include <sys/queue.h>
+#include <sys/mman.h>
+#include <event.h>
+#include <threadlib_internal.h>
+#include <threadlib.h>
+
+// Comment out, to enable debugging in this file
+#ifndef DEBUG_threadlib_c
+#undef debug
+#define debug(...)
+#undef tdebug
+#define tdebug(...)
+#endif
+
+// Thread local pointer to the user thread currently running on a given core. 
+// This variable is only meaningful to the context running
+// the scheduler code (i.e. vcore context in ROS, user thread or scheduler
+// context on linux depending on whether we use a scheduler thread or not)
+__thread thread_t* current_thread=NULL;
+
+/**
+ * Bootup constructors.  Make sure things are initialized in the proper order.
+ * Ideally we would be able to use the contsructor priorities instead of having
+ * to call them all back to back like this, but for some reason I couldn't get
+ * them to work properly. I.e. the priorities weren't being honored.
+ **/
+extern void read_config();
+extern void main_thread_init();
+extern void vcore_startup();
+void __attribute__ ((constructor)) ctors()
+{
+       
+//     init_cycle_clock();
+       read_config();
+       main_thread_init();
+       vcore_startup();
+}
+
+       
+       //uint32_t vc = vcore_id();
+       //uint32_t kvc = ros_syscall(SYS_getvcoreid, 0, 0, 0, 0, 0, 0);
+       //set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+       ///* Verify that the currently running thread is the one that the vcore thought
+       // * was running */
+       //if(current_thread != t)
+       //      printf("variable:\tthread:vcore\n"
+       //             "current_thread:\t%p:%p\n"
+       //             "vcore_id:\t%p:%p\n"
+       //             "SYS_getvcoreid:\t%p:%p\n",
+       //             t, current_thread, vc, vcore_id(), kvc, ros_syscall(SYS_getvcoreid, 0, 0, 0, 0, 0, 0)
+       //            );
+       //assert(current_thread == t);
+
+/**
+ * Initialize the vcores, including jumping into muticore mode.
+ **/
+void vcore_startup()
+{
+       /* Initilize the bootstrap code for using the vcores */
+       if (vcore_init())
+               printf("vcore_init() failed, we're fucked!\n");
+       assert(vcore_id() == 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);
+
+       /* Don't forget to enable notifs on vcore0.  if you don't, the kernel will
+        * restart your _S with notifs disabled, which is a path to confusion. */
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[0];
+       vcpd->notif_enabled = TRUE;
+
+       /* Grab a reference to the main_thread on the current stack (i.e.
+        * current_thread, since we know this has been set up for us properly by
+        * the fact that the constructor calls main_thread_init() before this
+        * function.  We will need this reference below. */
+       thread_t *t = current_thread;
+
+    /* Change temporarily to vcore0s tls region so we can save the main_thread
+        * into its thread local current_thread variable.  One minor issue is that
+        * vcore0's transition-TLS isn't TLS_INITed yet.  Until it is (right before
+        * vcore_entry(), don't try and take the address of any of its TLS vars. */
+       extern void** vcore_thread_control_blocks;
+       set_tls_desc(vcore_thread_control_blocks[0], 0);
+       current_thread = t;
+       set_tls_desc(t->context->tls_desc, 0);
+
+       /* Jump into multi-core mode! */
+       /* The next line of code that will run is inside vcore_entry().  When this
+        * thread is resumed, it will continue directly after this call to
+        * vcore_request() */
+       vcore_request(1);
+}
+
+/**
+ * Switch into vcore mode to run the scheduler code. 
+ **/
+void switch_to_vcore() {
+
+       uint32_t vcoreid = vcore_id();
+
+       /* Disable notifications.  Once we do this, we might miss a notif_pending,
+        * so we need to enter vcore entry later.  Need to disable notifs so we
+        * don't get in weird loops */
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       vcpd->notif_enabled = FALSE;
+
+       /* Grab a reference to the currently running thread on this vcore */
+       thread_t *t = current_thread; 
+
+       /* Switch to the vcore's tls region */
+       extern void** vcore_thread_control_blocks;
+       set_tls_desc(vcore_thread_control_blocks[vcoreid], vcoreid);
+       
+       /* Verify that the thread the vcore thinks was running is the same as the thread
+        * that was actually running */
+       assert(current_thread == t);
+
+       /* Set the stack pointer to the stack of the vcore. 
+        * We know this function is always inlined because of the attribute we set
+        * on it, so there will be no stack unwinding when this function "returns".
+        * After this call, make sure you don't use local variables. */
+       set_stack_pointer((void*)vcpd->transition_stack);
+       assert(in_vcore_context());
+
+       /* Leave the current vcore completely */
+       current_thread = NULL; 
+       
+       /* Restart the vcore and run the scheduler code */
+       vcore_entry();
+       assert(0);
+}
+
+/**
+ * Entry point for the vcore.  Basic job is to either resume the thread that
+ * was interrupted in the case of a notification coming in, or to find a new
+ * thread from the user level threading library and launch it.
+ **/
+void __attribute__((noreturn)) vcore_entry()
+{
+       /* Grab references to the current vcoreid vcore preemption data, and the
+     * vcoremap */
+       assert(in_vcore_context());
+       uint32_t vcoreid = vcore_id();
+       struct preempt_data *vcpd = &__procdata.vcore_preempt_data[vcoreid];
+       struct vcore *vc = &__procinfo.vcoremap[vcoreid];
+
+       tdebug("current=%s, vcore=%d\n",
+               current_thread?current_thread->name : "NULL", vcoreid);
+
+       /* Assert that notifications are disabled. Should always have notifications
+        * disabled when coming in here. */
+       assert(vcpd->notif_enabled == FALSE);
+
+       /* Put this in the loop that deals with notifications.  It will return if
+        * there is no preempt pending. */ 
+       if (vc->preempt_pending)
+               sys_yield(TRUE);
+
+       /* When running vcore_entry(), we are using the TLS of the vcore, not any
+        * particular thread.  If current_thread is set in the vcore's TLS, then 
+        * that means the thread did not yield voluntarily, and was, instead, 
+        * interrupted by a notification.  We therefore need to restore the thread
+        * context from the notification trapframe, not the one stored in the 
+        * thread struct itself. */
+    if (unlikely(current_thread)) {
+        vcpd->notif_pending = 0;
+        /* Do one last check for notifs after clearing pending */
+        // TODO: call the handle_notif() here (first)
+
+               /* Copy the notification trapframe into the current 
+                * threads trapframe */
+               memcpy(&current_thread->context->utf, &vcpd->notif_tf, 
+                      sizeof(struct user_trapframe));
+
+        /* Restore the context from the current_thread's trapframe */
+        restore_context(current_thread->context);
+        assert(0);
+    }
+
+       /* Otherwise either a vcore is coming up for the first time, or a thread
+        * has just yielded and vcore_entry() was called directly. In this case we 
+        * need to figure out which thread to schedule next on the vcore */
+       run_next_thread();
+       assert(0);
+}
+
index 4e3723e..05f4fdb 100644 (file)
@@ -159,7 +159,7 @@ static long long timing_loop()
 }
 
 
-void init_cycle_clock() __attribute__((constructor));
+//void init_cycle_clock() __attribute__((constructor));
 void init_cycle_clock(void)
 {
   static int init_done = 0;
index 3f4915d..54d743e 100644 (file)
@@ -7,6 +7,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "threadlib.h"
 #include "debug.h"
 #include "config.h"
 
@@ -49,7 +50,6 @@ static inline int bool_value(char *str)
 
 
 // for now, just read from env vars.  Add config file option later.
-void read_config(void) __attribute__((constructor));
 void read_config(void) {
   static int read_config_done = 0;
   
index c3cd444..5b22219 100644 (file)
@@ -14,6 +14,7 @@
 #include "debug.h"
 #include "clock.h"
 #include "config.h"
+#include <vcore.h>
 
 
 #define DBG_NO_TIMING -12312
@@ -26,7 +27,7 @@ cpu_tick_t ticks_rdiff = 0;
 static cpu_tick_t vnow_prev = 0;
 static cpu_tick_t vrnow_prev = 0;
 
-void init_debug() __attribute__((constructor));
+//void init_debug() __attribute__((constructor));
 void init_debug() {
   init_cycle_clock();
   vnow_prev  = virtual_start_ticks;
@@ -69,6 +70,13 @@ static inline void output_aux(int tid, const char *func, const char *fmt, va_lis
     len += ret;
   }
 
+  // add the vcore number
+  if( func ) {
+    ret = snprintf(str+len, sizeof(str)-1-len, "vcore %d - ", vcore_id());
+    assert(ret > 0);
+    len += ret;
+  }
+
   // add the function name
   if( func ) {
     ret = snprintf(str+len, sizeof(str)-1-len, "%s() - ",func);
index ac5f460..98ac3e2 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <stdlib.h>
 #include <sys/syscall.h>
+#include <assert.h>
 #include <stdarg.h>
 #include <errno.h>
 #include <string.h>
@@ -40,17 +41,6 @@ do { \
 } while(0)
 */
 
-#undef assert
-#if OPTIMIZE < 2
-void assert_failed(char *file, unsigned int line, const char *func, char *expr);
-#define assert(expr) \
-if( !(expr) ) { \
-  assert_failed(__FILE__, __LINE__, __FUNCTION__, __STRING(expr)); \
-}
-#else
-#define assert(expr) do { } while(0)
-#endif
-
 void real_debug(const char *func, const char *fmt, ...) __attribute__ ((format (printf,2,3)));
 void real_toutput(int tid, const char *func, const char *fmt, ...) __attribute__ ((format (printf,3,4)));
 void output(char *fmt, ...) __attribute__ ((format (printf,1,2)));