]> Zhao Yanbai Git Server - minix.git/commitdiff
Revamp the mthread library and update test59
authorThomas Veerman <thomas@minix3.org>
Thu, 30 Sep 2010 13:44:13 +0000 (13:44 +0000)
committerThomas Veerman <thomas@minix3.org>
Thu, 30 Sep 2010 13:44:13 +0000 (13:44 +0000)
Before, the 'main thread' of a process was never taken into account anywhere in
the library, causing mutexes not to work properly (and consequently, neither
did the condition variables). For example, if the 'main thread' (that is, the
thread which is started at the beginning of a process; not a spawned thread by
the library) would lock a mutex, it wasn't actually locked.

include/minix/mthread.h
lib/libmthread/allocate.c
lib/libmthread/attribute.c
lib/libmthread/condition.c
lib/libmthread/global.h
lib/libmthread/misc.c
lib/libmthread/mutex.c
lib/libmthread/proto.h
lib/libmthread/queue.c
lib/libmthread/scheduler.c
test/test59.c

index e98cd8d211790cb7f0f202b0bbfbdce559c20976..7549515d4a271ab6f0e7845c680905237cacffd1 100644 (file)
@@ -17,14 +17,15 @@ typedef int mthread_once_t;
 typedef void * mthread_condattr_t;
 typedef void * mthread_mutexattr_t;
 
+struct __mthread_tcb;
 typedef struct {
-  mthread_thread_t head;
-  mthread_thread_t tail;
+  struct __mthread_tcb *head;
+  struct __mthread_tcb *tail;
 } mthread_queue_t;
 
 struct __mthread_mutex {
-  mthread_queue_t queue;       /* Threads blocked on this mutex */
-  mthread_thread_t owner;      /* Thread that currently owns mutex */
+  mthread_queue_t queue;       /* Queue of threads blocked on this mutex */
+  mthread_thread_t owner;      /* Thread ID that currently owns mutex */
   struct __mthread_mutex *prev;
   struct __mthread_mutex *next;
 };
@@ -37,10 +38,6 @@ struct __mthread_cond {
 };
 typedef struct __mthread_cond *mthread_cond_t;
 
-typedef enum {
-  CONDITION, DEAD, EXITING, FALLBACK_EXITING, MUTEX, RUNNABLE
-} mthread_state_t;
-
 struct __mthread_attr {
   size_t a_stacksize;
   char *a_stackaddr;
@@ -50,22 +47,6 @@ struct __mthread_attr {
 }; 
 typedef struct __mthread_attr *mthread_attr_t;
 
-typedef struct {
-  mthread_thread_t m_next;             /* Next thread to run */
-  mthread_state_t m_state;             /* Thread state */
-  struct __mthread_attr m_attr;                /* Thread attributes */
-  struct __mthread_cond *m_cond;       /* Condition variable that this thread
-                                        * might be blocking on */
-  void *(*m_proc)(void *);             /* Procedure to run */
-  void *m_arg;                         /* Argument passed to procedure */
-  void *m_result;                      /* Result after procedure returns */
-  mthread_cond_t m_exited;             /* Condition variable signaling this
-                                        * thread has ended */
-  mthread_mutex_t m_exitm;             /* Mutex to accompany exit condition */
-  ucontext_t m_context;                        /* Thread machine context */
-} mthread_tcb_t;
-
-#define NO_THREAD -1
 #define MTHREAD_CREATE_JOINABLE 001
 #define MTHREAD_CREATE_DETACHED 002
 #define MTHREAD_ONCE_INIT 0
@@ -120,23 +101,18 @@ _PROTOTYPE( void mthread_verify_f, (char *f, int l)                                       );
 _PROTOTYPE( int mthread_mutex_destroy, (mthread_mutex_t *mutex)        );
 _PROTOTYPE( int mthread_mutex_init, (mthread_mutex_t *mutex,
                                     mthread_mutexattr_t *mattr)        );
+#if 0
 _PROTOTYPE( int mthread_mutex_lock, (mthread_mutex_t *mutex)           );
+#endif
+_PROTOTYPE( int mthread_mutex_lock_f, (mthread_mutex_t *mutex,
+                                       char *file, int line)           );
+#define mthread_mutex_lock(x) mthread_mutex_lock_f(x, __FILE__, __LINE__)
 _PROTOTYPE( int mthread_mutex_trylock, (mthread_mutex_t *mutex)        );
 _PROTOTYPE( int mthread_mutex_unlock, (mthread_mutex_t *mutex) );
 
 /* schedule.c */
-_PROTOTYPE( void mthread_schedule, (void)                              );
-_PROTOTYPE( void mthread_suspend, (mthread_state_t state)              );
-_PROTOTYPE( void mthread_unsuspend, (mthread_thread_t thread)          );
 _PROTOTYPE( void mthread_init, (void)                                  );
 _PROTOTYPE( int mthread_yield, (void)                                  );
 _PROTOTYPE( void mthread_yield_all, (void)                             );
 
-/* queue.c */
-_PROTOTYPE( void mthread_queue_init, (mthread_queue_t *queue)          );
-_PROTOTYPE( void mthread_queue_add, (mthread_queue_t *queue, 
-                                    mthread_thread_t thread)           );
-_PROTOTYPE( mthread_thread_t mthread_queue_remove, (mthread_queue_t *queue));
-_PROTOTYPE( int mthread_queue_isempty, (mthread_queue_t *queue)        );
-
 #endif
index 2c769310e137c0742617cc3fcca0a6435b7a5d86..29bb188cae7a03d7802d65bc8b68a9898ca09d0e 100644 (file)
@@ -87,22 +87,25 @@ mthread_thread_t detach;
 /* Mark a thread as detached. Consequently, upon exit, resources allocated for
  * this thread are automatically freed.
  */
-
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_tcb_t *tcb;
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (!isokthreadid(detach)) {
        errno = ESRCH;
        return(-1);
-  } else if (threads[detach].m_state == DEAD) {
+  }
+
+  tcb = mthread_find_tcb(detach);
+  if (tcb->m_state == MS_DEAD) {
        errno = ESRCH;
        return(-1);
-  } else if (threads[detach].m_attr.a_detachstate != MTHREAD_CREATE_DETACHED) {
-       if (threads[detach].m_state == EXITING) {
+  } else if (tcb->m_attr.a_detachstate != MTHREAD_CREATE_DETACHED) {
+       if (tcb->m_state == MS_EXITING) 
                mthread_thread_stop(detach);
-       } else {
-               threads[detach].m_attr.a_detachstate = MTHREAD_CREATE_DETACHED;
-       }
+       else 
+               tcb->m_attr.a_detachstate = MTHREAD_CREATE_DETACHED;
   }
+
   return(0);
 }
 
@@ -115,30 +118,30 @@ void *value;
 {
 /* Make a thread stop running and store the result value. */
   int fallback_exit = 0;
-  mthread_thread_t stop;
+  mthread_tcb_t *tcb;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
-  stop = current_thread;
+  tcb = mthread_find_tcb(current_thread);
 
-  if (threads[stop].m_state == EXITING) /* Already stopping, nothing to do. */
+  if (tcb->m_state == MS_EXITING)      /* Already stopping, nothing to do. */
        return;
 
   /* When we're called from the fallback thread, the fallback thread 
    * will invoke the scheduler. However, if the thread itself called 
    * mthread_exit, _we_ will have to wake up the scheduler.
    */
-  if (threads[stop].m_state == FALLBACK_EXITING)
+  if (tcb->m_state == MS_FALLBACK_EXITING)
        fallback_exit = 1;
 
-  threads[stop].m_result = value;
-  threads[stop].m_state = EXITING;
+  tcb->m_result = value;
+  tcb->m_state = MS_EXITING;
 
-  if (threads[stop].m_attr.a_detachstate == MTHREAD_CREATE_DETACHED) {
-       mthread_thread_stop(stop);
+  if (tcb->m_attr.a_detachstate == MTHREAD_CREATE_DETACHED) {
+       mthread_thread_stop(current_thread);
   } else {
        /* Joinable thread; notify possibly waiting thread */
-       if (mthread_cond_signal(&(threads[stop].m_exited)) != 0) 
+       if (mthread_cond_signal(&(tcb->m_exited)) != 0) 
                mthread_panic("Couldn't signal exit");
 
        /* The thread that's actually doing the join will eventually clean
@@ -160,15 +163,18 @@ void *value;
  *===========================================================================*/
 PRIVATE void mthread_fallback(void)
 {
-/* The mthreads fallback thread. The idea is that every thread calls 
+/* The libmthread fallback thread. The idea is that every thread calls 
  * mthread_exit(...) to stop running when it has nothing to do anymore. 
  * However, in case a thread forgets to do that, the whole process  exit()s and
  * that might be a bit problematic. Therefore, all threads will run this
  * fallback thread when they exit, giving the scheduler a chance to fix the
  * situation.
  */
+  mthread_tcb_t *tcb;
+
+  tcb = mthread_find_tcb(current_thread);
 
-  threads[current_thread].m_state = FALLBACK_EXITING;
+  tcb->m_state = MS_FALLBACK_EXITING;
   mthread_exit(NULL);
 
   /* Reconstruct fallback context for next invocation */
@@ -176,7 +182,25 @@ PRIVATE void mthread_fallback(void)
 
   /* Let another thread run */
   mthread_schedule();
+}
+
 
+/*===========================================================================*
+ *                     mthread_find_tcb                                     *
+ *===========================================================================*/
+PUBLIC mthread_tcb_t * mthread_find_tcb(thread)
+mthread_thread_t thread;
+{
+  mthread_tcb_t *rt = NULL;
+
+  if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
+
+  if (thread == MAIN_THREAD)
+       rt = &mainthread;
+  else
+       rt = threads[thread];
+
+  return(rt);
 }
 
 
@@ -186,23 +210,44 @@ PRIVATE void mthread_fallback(void)
 PRIVATE int mthread_increase_thread_pool(void)
 {
 /* Increase thread pool. No fancy algorithms, just double the size. */
-  mthread_tcb_t *new_tcb;
+  mthread_tcb_t **new_tcb;
   int new_no_threads, old_no_threads, i;
 
   old_no_threads = no_threads;
-  new_no_threads = 2 * old_no_threads;
+
+  if (old_no_threads == 0)
+       new_no_threads = NO_THREADS;
+  else
+       new_no_threads = 2 * old_no_threads;
+
 
   if (new_no_threads >= MAX_THREAD_POOL) {
        mthread_debug("Reached max number of threads");
        return(-1);
   }
 
-  new_tcb = realloc(threads, new_no_threads * sizeof(mthread_tcb_t));
+  /* Allocate space to store pointers to thread control blocks */
+  if (old_no_threads == 0)     /* No data yet: allocate space */
+       new_tcb = calloc(new_no_threads, sizeof(mthread_tcb_t *));
+  else                         /* Preserve existing data: reallocate space */
+       new_tcb = realloc(threads, new_no_threads * sizeof(mthread_tcb_t *));
+
   if (new_tcb == NULL) {
        mthread_debug("Can't increase thread pool");
        return(-1);
   }
 
+  /* Allocate space for thread control blocks itself */
+  for (i = old_no_threads; i < new_no_threads; i++) {
+       new_tcb[i] = malloc(sizeof(mthread_tcb_t));
+       if (new_tcb[i] == NULL) {
+               mthread_debug("Can't allocate space for tcb");
+               return(-1);
+       }
+       memset(new_tcb[i], '\0', sizeof(mthread_tcb_t)); /* Clear entry */
+  }
+
+  /* We can breath again, let's tell the others about the good news */
   threads = new_tcb; 
   no_threads = new_no_threads;
 
@@ -213,7 +258,8 @@ PRIVATE int mthread_increase_thread_pool(void)
   }
 
 #ifdef MDEBUG
-  printf("Increased thread pool from %d to %d threads\n", no_threads, new_no_threads);
+  printf("Increased thread pool from %d to %d threads\n", old_no_threads,
+        new_no_threads);
 #endif
   return(0);
 }
@@ -230,7 +276,7 @@ PUBLIC void mthread_init(void)
 
   if (!initialized) {
        int i;
-       no_threads = NO_THREADS;
+       no_threads = 0;
        used_threads = 0;
        running_main_thread = 1;/* mthread_init can only be called from the
                                 * main thread. Calling it from a thread will
@@ -239,34 +285,20 @@ PUBLIC void mthread_init(void)
 
        if (getcontext(&(mainthread.m_context)) == -1)
                mthread_panic("Couldn't save state for main thread");
-       current_thread = NO_THREAD;
-
-       /* Allocate a bunch of thread control blocks */
-       threads = malloc(no_threads * sizeof(mthread_tcb_t));
-       if (threads == NULL)
-               mthread_panic("No memory, can't initialize threads");
+       current_thread = MAIN_THREAD;
 
        mthread_init_valid_mutexes();
        mthread_init_valid_conditions();
        mthread_init_valid_attributes();
        mthread_init_scheduler();
 
-       /* Put initial threads on the free threads queue */
-       mthread_queue_init(&free_threads);
-       for (i = 0; i < no_threads; i++) {
-               mthread_queue_add(&free_threads, i);
-               mthread_thread_reset(i);
-       }
-
        /* Initialize the fallback thread */
        if (getcontext(FALLBACK_CTX) == -1)
                mthread_panic("Could not initialize fallback thread");
        FALLBACK_CTX->uc_link = &(mainthread.m_context);
-       FALLBACK_CTX->uc_stack.ss_sp = malloc(STACKSZ);
+       FALLBACK_CTX->uc_stack.ss_sp = fallback_stack;
        FALLBACK_CTX->uc_stack.ss_size = STACKSZ;
-       if (FALLBACK_CTX->uc_stack.ss_sp == NULL)
-               mthread_panic("Could not allocate stack space to fallback "
-                             "thread");
+       memset(fallback_stack, '\0', STACKSZ);
        makecontext(FALLBACK_CTX, (void (*) (void)) mthread_fallback, 0);
 
        initialized = 1;
@@ -283,7 +315,9 @@ void **value;
 {
 /* Wait for a thread to stop running and copy the result. */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_tcb_t *tcb;
+
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (!isokthreadid(join)) {
        errno = ESRCH;
@@ -291,21 +325,24 @@ void **value;
   } else if (join == current_thread) {
        errno = EDEADLK;
        return(-1);
-  } else if (threads[join].m_state == DEAD) {
+  }
+
+  tcb = mthread_find_tcb(join);
+  if (tcb->m_state == MS_DEAD) {
        errno = ESRCH;
        return(-1);
-  } else if (threads[join].m_attr.a_detachstate == MTHREAD_CREATE_DETACHED) {
+  } else if (tcb->m_attr.a_detachstate == MTHREAD_CREATE_DETACHED) {
        errno = EINVAL;
        return(-1);
   }
 
   /* When the thread hasn't exited yet, we have to wait for that to happen */
-  if (threads[join].m_state != EXITING) {
+  if (tcb->m_state != MS_EXITING) {
        mthread_cond_t *c;
        mthread_mutex_t *m;
 
-       c = &(threads[join].m_exited);
-       m = &(threads[join].m_exitm);
+       c = &(tcb->m_exited);
+       m = &(tcb->m_exitm);
 
        if (mthread_mutex_init(m, NULL) != 0)
                mthread_panic("Couldn't initialize mutex to join\n");
@@ -325,7 +362,7 @@ void **value;
 
   /* Thread has exited; copy results */
   if(value != NULL)
-       *value = threads[join].m_result;
+       *value = tcb->m_result;
 
   /* Deallocate resources */
   mthread_thread_stop(join);
@@ -342,7 +379,7 @@ void (*proc)(void);
 {
 /* Run procedure proc just once */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (once == NULL || proc == NULL) {
        errno = EINVAL;
@@ -362,7 +399,7 @@ PUBLIC mthread_thread_t mthread_self(void)
 {
 /* Return the thread id of the thread calling this function. */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   return(current_thread);
 }
@@ -381,50 +418,52 @@ void *arg;
  * procedure with the given parameter. The thread is marked as runnable.
  */
 
-#define THIS_CTX (&(threads[thread].m_context))
+#define THIS_CTX (&(threads[thread]->m_context))
+  mthread_tcb_t *tcb;
   size_t stacksize;
   char *stackaddr;
 
-  threads[thread].m_next = NO_THREAD;
-  threads[thread].m_state = DEAD;
-  threads[thread].m_proc = (void *(*)(void *)) proc; /* Yikes */
-  threads[thread].m_arg = arg;
+  tcb = mthread_find_tcb(thread);
+  tcb->m_next = NULL;
+  tcb->m_state = MS_DEAD;
+  tcb->m_proc = (void *(*)(void *)) proc; /* Yikes */
+  tcb->m_arg = arg;
   /* Threads use a copy of the provided attributes. This way, if another
    * thread modifies the attributes (such as detach state), already running
    * threads are not affected.
    */
   if (tattr != NULL)
-       threads[thread].m_attr = *((struct __mthread_attr *) *tattr);
+       tcb->m_attr = *((struct __mthread_attr *) *tattr);
   else {
-       threads[thread].m_attr = default_attr;
+       tcb->m_attr = default_attr;
   }
 
-  if (mthread_cond_init(&(threads[thread].m_exited), NULL) != 0)
+  if (mthread_cond_init(&(tcb->m_exited), NULL) != 0)
        mthread_panic("Could not initialize thread");
 
   /* First set the fallback thread, */
-  THIS_CTX->uc_link = FALLBACK_CTX;
+  tcb->m_context.uc_link = FALLBACK_CTX;
 
   /* then construct this thread's context to run procedure proc. */
-  if (getcontext(THIS_CTX) == -1)
+  if (getcontext(&(tcb->m_context)) == -1)
        mthread_panic("Failed to initialize context state");
 
-  stacksize = threads[thread].m_attr.a_stacksize;
-  stackaddr = threads[thread].m_attr.a_stackaddr;
+  stacksize = tcb->m_attr.a_stacksize;
+  stackaddr = tcb->m_attr.a_stackaddr;
 
   if (stacksize == (size_t) 0)
        stacksize = (size_t) MTHREAD_STACK_MIN;
 
   if (stackaddr == NULL) {
        /* Allocate stack space */
-       THIS_CTX->uc_stack.ss_sp = malloc(stacksize);
-       if (THIS_CTX->uc_stack.ss_sp == NULL)
+       tcb->m_context.uc_stack.ss_sp = malloc(stacksize);
+       if (tcb->m_context.uc_stack.ss_sp == NULL)
                mthread_panic("Failed to allocate stack to thread");
   } else
-       THIS_CTX->uc_stack.ss_sp = stackaddr;
+       tcb->m_context.uc_stack.ss_sp = stackaddr;
 
-  THIS_CTX->uc_stack.ss_size = stacksize;
-  makecontext(THIS_CTX, mthread_trampoline, 0);
+  tcb->m_context.uc_stack.ss_size = stacksize;
+  makecontext(&(tcb->m_context), mthread_trampoline, 0);
 
   mthread_unsuspend(thread); /* Make thread runnable */
 }
@@ -441,10 +480,10 @@ mthread_thread_t thread;
   mthread_tcb_t *rt;
   if (!isokthreadid(thread)) mthread_panic("Invalid thread id"); 
 
-  rt = &(threads[thread]);
-
-  rt->m_next = NO_THREAD;
-  rt->m_state = DEAD;
+  rt = mthread_find_tcb(thread);
+  rt->m_tid = thread;
+  rt->m_next = NULL;
+  rt->m_state = MS_DEAD;
   rt->m_proc = NULL;
   rt->m_arg = NULL;
   rt->m_result = NULL;
@@ -468,10 +507,10 @@ mthread_thread_t thread;
 
   if (!isokthreadid(thread)) mthread_panic("Invalid thread id"); 
 
-  stop_thread = &(threads[thread]);
+  stop_thread = mthread_find_tcb(thread);
 
-  if (stop_thread->m_state == DEAD) {
-       /* Already DEAD, nothing to do */
+  if (stop_thread->m_state == MS_DEAD) {
+       /* Already dead, nothing to do */
        return;
   }
 
@@ -492,9 +531,12 @@ PRIVATE void mthread_trampoline(void)
 {
 /* Execute the /current_thread's/ procedure. Store its result. */
 
+  mthread_tcb_t *tcb;
   void *r;
 
-  r = (threads[current_thread].m_proc)(threads[current_thread].m_arg);
+  tcb = mthread_find_tcb(current_thread);
+
+  r = (tcb->m_proc)(tcb->m_arg);
   mthread_exit(r); 
 }
 
index b696640782201a9301189b231ccb9a8bdd751c91..dda0aaf3d1225fbd4ba490757e475d8c417ad2fe 100644 (file)
@@ -1,6 +1,6 @@
 #include <minix/mthread.h>
-#include "proto.h"
 #include "global.h"
+#include "proto.h"
 
 PRIVATE struct __mthread_attr *va_front, *va_rear;
 FORWARD _PROTOTYPE( void mthread_attr_add, (mthread_attr_t *a)         );
index e86380fcf36278848da8050d0e31b21919c4fdae..bb8a6e341e310144f4e7b437fa775b7ba1434d15 100644 (file)
@@ -1,12 +1,12 @@
 #include <minix/mthread.h>
-#include "proto.h"
 #include "global.h"
+#include "proto.h"
 
 PRIVATE struct __mthread_cond *vc_front, *vc_rear;
 FORWARD _PROTOTYPE( void mthread_cond_add, (mthread_cond_t *c)         );
 FORWARD _PROTOTYPE( void mthread_cond_remove, (mthread_cond_t *c)      );
 FORWARD _PROTOTYPE( int mthread_cond_valid, (mthread_cond_t *c)        );
-
+#define MAIN_COND mainthread.m_cond
 
 /*===========================================================================*
  *                             mthread_init_valid_conditions                *
@@ -46,9 +46,10 @@ PUBLIC int mthread_cond_broadcast(cond)
 mthread_cond_t *cond;
 {
 /* Signal all threads waiting for condition 'cond'. */
-  int i;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if(cond == NULL) {
        errno = EINVAL;
@@ -60,9 +61,15 @@ mthread_cond_t *cond;
        return(-1);
   }
 
-  for (i = 0; i < no_threads; i++) 
-       if (threads[i].m_state == CONDITION && threads[i].m_cond == *cond) 
-               mthread_unsuspend(i);
+  tcb = mthread_find_tcb(MAIN_THREAD);
+  if (tcb->m_state == MS_CONDITION && tcb->m_cond == *cond)
+       mthread_unsuspend(MAIN_THREAD);
+
+  for (t = (mthread_thread_t) 0; t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       if (tcb->m_state == MS_CONDITION && tcb->m_cond == *cond) 
+               mthread_unsuspend(t);
+  }
 
   return(0);
 }
@@ -75,9 +82,10 @@ PUBLIC int mthread_cond_destroy(cond)
 mthread_cond_t *cond;
 {
 /* Destroy a condition variable. Make sure it's not in use */
-  int i;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (cond == NULL) { 
        errno = EINVAL;
@@ -90,8 +98,15 @@ mthread_cond_t *cond;
   }
 
   /* Is another thread currently using this condition variable? */
-  for (i = 0; i < no_threads; i++) {
-       if (threads[i].m_state == CONDITION && threads[i].m_cond == *cond) {
+  tcb = mthread_find_tcb(MAIN_THREAD);
+  if (tcb->m_state == MS_CONDITION && tcb->m_cond == *cond) {
+       errno = EBUSY;
+       return(-1);
+  }
+
+  for (t = (mthread_thread_t) 0; t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       if(tcb->m_state == MS_CONDITION && tcb->m_cond == *cond){
                errno = EBUSY;
                return(-1);
        }
@@ -116,7 +131,7 @@ mthread_condattr_t *cattr;
 /* Initialize condition variable to a known state. cattr is ignored */
   struct __mthread_cond *c;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (cond == NULL) {
        errno = EINVAL;
@@ -171,9 +186,10 @@ PUBLIC int mthread_cond_signal(cond)
 mthread_cond_t *cond;
 {
 /* Signal a thread that condition 'cond' was met. Just a single thread. */
-  int i;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if(cond == NULL) {
        errno = EINVAL;
@@ -185,9 +201,14 @@ mthread_cond_t *cond;
        return(-1);
   }
 
-  for (i = 0; i < no_threads; i++) {
-       if (threads[i].m_state == CONDITION && threads[i].m_cond == *cond) {
-               mthread_unsuspend(i);
+  tcb = mthread_find_tcb(MAIN_THREAD);
+  if (tcb->m_state == MS_CONDITION && tcb->m_cond == *cond)
+       mthread_unsuspend(MAIN_THREAD);
+
+  for (t = (mthread_thread_t) 0; t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       if(tcb->m_state == MS_CONDITION && tcb->m_cond == *cond){
+               mthread_unsuspend(t);
                break;
        }
   }
@@ -226,7 +247,7 @@ PUBLIC int mthread_cond_verify(void)
 {
 /* Return true in case no condition variables are in use. */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   return(vc_front == NULL);
 }
@@ -241,10 +262,11 @@ mthread_cond_t *cond;
 mthread_mutex_t *mutex;
 {
 /* Wait for a condition to be signaled */
+  mthread_tcb_t *tcb;
   struct __mthread_cond *c;
   struct __mthread_mutex *m;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (cond == NULL || mutex == NULL) {
        errno = EINVAL;
@@ -263,13 +285,13 @@ mthread_mutex_t *mutex;
   if (mthread_mutex_unlock(mutex) != 0) /* Fails when we're not the owner */
        return(-1);
 
-  threads[current_thread].m_cond = c; /* Register condition variable. */
-
-  mthread_suspend(CONDITION);
+  tcb = mthread_find_tcb(current_thread);
+  tcb->m_cond = c; /* Register condition variable. */
+  mthread_suspend(MS_CONDITION);
 
   /* When execution returns here, the condition was met. Lock mutex again. */
   c->mutex = NULL;                             /* Forget about this mutex */
-  threads[current_thread].m_cond = NULL;       /* ... and condition var */
+  tcb->m_cond = NULL;                          /* ... and condition var */
   if (mthread_mutex_lock(mutex) != 0)
        return(-1);
 
index 0dc58716b81cf0f23cc2e5afc2f5fb8d06ada8bf..195622f504af6608e1a834a1854ebadf85556bca 100644 (file)
@@ -6,20 +6,42 @@
 
 #include <assert.h>
 
-#define NO_THREADS 3
-#define MAX_THREAD_POOL 1000
+#define NO_THREADS 
+#define MAX_THREAD_POOL 1024
 #define STACKSZ 4096
-#define isokthreadid(i)        (i >= 0 && i < no_threads)
+#define MAIN_THREAD -1
+#define NO_THREAD -2
+#define isokthreadid(i)        (i == MAIN_THREAD || (i >= 0 && i < no_threads))
+
+typedef enum {
+  MS_CONDITION, MS_DEAD, MS_EXITING, MS_FALLBACK_EXITING, MS_MUTEX, MS_RUNNABLE
+} mthread_state_t;
+
+struct __mthread_tcb {
+  mthread_thread_t m_tid;              /* My own ID */
+  mthread_state_t m_state;             /* Thread state */
+  struct __mthread_attr m_attr;                /* Thread attributes */
+  struct __mthread_cond *m_cond;       /* Condition variable that this thread
+                                        * might be blocking on */
+  void *(*m_proc)(void *);             /* Procedure to run */
+  void *m_arg;                         /* Argument passed to procedure */
+  void *m_result;                      /* Result after procedure returns */
+  mthread_cond_t m_exited;             /* Condition variable signaling this
+                                        * thread has ended */
+  mthread_mutex_t m_exitm;             /* Mutex to accompany exit condition */
+  ucontext_t m_context;                        /* Thread machine context */
+  struct __mthread_tcb *m_next;                /* Next thread in linked list */
+};
+typedef struct __mthread_tcb mthread_tcb_t;
 
 EXTERN mthread_thread_t current_thread;
-EXTERN int ret_code;
 EXTERN mthread_queue_t free_threads;
 EXTERN mthread_queue_t run_queue;              /* FIFO of runnable threads */
-EXTERN mthread_tcb_t *scheduler;
-EXTERN mthread_tcb_t *threads;
+EXTERN mthread_tcb_t **threads;
 EXTERN mthread_tcb_t fallback;
 EXTERN mthread_tcb_t mainthread;
 EXTERN int no_threads;
 EXTERN int used_threads;
 EXTERN int running_main_thread;
+EXTERN char fallback_stack[STACKSZ];
 
index 230bd8bb87b9790ed4167a77a880dabb2dd084a8..81301f31bef1898cca504713c84433eec68a0321 100644 (file)
@@ -1,7 +1,7 @@
 #include <minix/mthread.h>
 #include <stdio.h>
-#include "proto.h"
 #include "global.h"
+#include "proto.h"
 
 /*===========================================================================*
  *                             mthread_debug_f                              *
@@ -21,9 +21,11 @@ PUBLIC void mthread_debug_f(const char *file, int line, const char *msg)
 PUBLIC void mthread_panic_f(const char *file, int line, const char *msg)
 {
   /* Print panic message to stdout and exit */
-  printf("mthreads panic (%s:%d): ", file, line);
+  printf("mthread panic (%s:%d): ", file, line);
   printf(msg);
   printf("\n");
+  fflush(stdout);      /* Force debug print to screen */
+  *((int *)0) = 1;     /* Cause segfault to generate trace */
   exit(1);
 }
 
@@ -39,11 +41,14 @@ PUBLIC void mthread_verify_f(char *file, int line)
    * quiescent; no mutexes, conditions, or threads in use. All threads are to
    * be in DEAD state.
    */
-  int i;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
   int threads_ok = 1, conditions_ok = 1, mutexes_ok = 1, attributes_ok = 1;
 
-  for (i = 0; threads_ok && i < no_threads; i++)
-       if (threads[i].m_state != DEAD) threads_ok = 0;
+  for (t = (mthread_thread_t) 0; threads_ok && t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       if (tcb->m_state != MS_DEAD) threads_ok = 0;
+  }
 
   conditions_ok = mthread_cond_verify();
   mutexes_ok = mthread_mutex_verify();
@@ -69,17 +74,20 @@ PUBLIC void mthread_verify_f(char *f, int l) { ; }
  *===========================================================================*/
 PUBLIC void mthread_stats(void)
 {
-  int i, st_run, st_dead, st_cond, st_mutex, st_exit, st_fbexit;;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
+  int st_run, st_dead, st_cond, st_mutex, st_exit, st_fbexit;
   st_run = st_dead = st_cond = st_mutex = st_exit = st_fbexit = 0;
 
-  for (i = 0; i < no_threads; i++) {
-       switch(threads[i].m_state) {
-               case RUNNABLE: st_run++; break;
-               case DEAD: st_dead++; break;
-               case MUTEX: st_mutex++; break;
-               case CONDITION: st_cond++; break;
-               case EXITING: st_exit++; break;
-               case FALLBACK_EXITING: st_fbexit++; break;
+  for (t = (mthread_thread_t) 0; t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       switch(tcb->m_state) {
+               case MS_RUNNABLE: st_run++; break;
+               case MS_DEAD: st_dead++; break;
+               case MS_MUTEX: st_mutex++; break;
+               case MS_CONDITION: st_cond++; break;
+               case MS_EXITING: st_exit++; break;
+               case MS_FALLBACK_EXITING: st_fbexit++; break;
                default: mthread_panic("Unknown state");
        }
   }
index 059744056fdd249f741f1a1f729d6d3c6b063def..06d1afab6ddb6ecd5559aeccc69dcd2dd1a5c5a6 100644 (file)
@@ -1,6 +1,6 @@
 #include <minix/mthread.h>
-#include "proto.h"
 #include "global.h"
+#include "proto.h"
 
 PRIVATE struct __mthread_mutex *vm_front, *vm_rear;
 FORWARD _PROTOTYPE( void mthread_mutex_add, (mthread_mutex_t *m)       );
@@ -45,7 +45,8 @@ mthread_mutex_t *mutex;
 {
 /* Invalidate mutex and deallocate resources. */
 
-  int i;
+  mthread_thread_t t;
+  mthread_tcb_t *tcb;
 
   mthread_init();      /* Make sure mthreads is initialized */
 
@@ -58,15 +59,16 @@ mthread_mutex_t *mutex;
        errno = EINVAL;
        return(-1);
   } else if ((*mutex)->owner != NO_THREAD) {
+       printf("mutex owner is %d, so not destroying\n", (*mutex)->owner);
        errno = EBUSY;
        return(-1);
   }
 
   /* Check if this mutex is not associated with a condition */
-  for (i = 0; i < no_threads; i++) {
-       if (threads[i].m_state == CONDITION) {
-               if (threads[i].m_cond != NULL &&
-                   threads[i].m_cond->mutex == *mutex) {
+  for (t = (mthread_thread_t) 0; t < no_threads; t++) {
+       tcb = mthread_find_tcb(t);
+       if (tcb->m_state == MS_CONDITION) {
+               if (tcb->m_cond != NULL && tcb->m_cond->mutex == *mutex) {
                        errno = EBUSY;
                        return(-1);
                }
@@ -120,8 +122,10 @@ mthread_mutexattr_t *mattr;        /* Mutex attribute */
 /*===========================================================================*
  *                             mthread_mutex_lock                           *
  *===========================================================================*/
-PUBLIC int mthread_mutex_lock(mutex)
+PUBLIC int mthread_mutex_lock_f(mutex, file, line)
 mthread_mutex_t *mutex;        /* Mutex that is to be locked */
+char file[NAME_MAX + 1];
+int line;
 {
 /* Try to lock this mutex. If already locked, append the current thread to
  * FIFO queue associated with this mutex and suspend the thread. */
@@ -141,12 +145,16 @@ mthread_mutex_t *mutex;   /* Mutex that is to be locked */
        return(-1);
   } else if (m->owner == NO_THREAD) { /* Not locked */
        m->owner = current_thread;
+       if (current_thread == MAIN_THREAD)
+               mthread_debug("MAIN_THREAD now mutex owner\n");
   } else if (m->owner == current_thread) {
        errno = EDEADLK;
        return(-1);
   } else {
        mthread_queue_add( &(m->queue), current_thread);
-       mthread_suspend(MUTEX);
+       if (m->owner == MAIN_THREAD)
+               mthread_dump_queue(&(m->queue));
+       mthread_suspend(MS_MUTEX);
   }
 
   /* When we get here we acquired the lock. */
@@ -277,6 +285,7 @@ PUBLIC int mthread_mutex_verify(void)
   loopitem = vm_front;
 
   while (loopitem != NULL) {
+       printf("mutex corruption: owner: %d\n", loopitem->owner);
        loopitem = loopitem->next;
        r = 0;
   }
index 3d77a65902da0e7b1c30ec1565055e29e2808cd4..7ef4aa27b0a11cc4b29426a2d64abcc022d9ef96 100644 (file)
@@ -1,6 +1,9 @@
 #ifndef __MTHREAD_PROTO_H__
 #define __MTHREAD_PROTO_H__
 
+/* allocate.c */
+_PROTOTYPE( mthread_tcb_t * mthread_find_tcb, (mthread_thread_t thread)        );
+
 /* attribute.c */
 _PROTOTYPE( void mthread_init_valid_attributes, (void)                 );
 #ifdef MDEBUG
@@ -28,12 +31,19 @@ _PROTOTYPE( int mthread_mutex_valid, (mthread_mutex_t *mutex)               );
 _PROTOTYPE( int mthread_mutex_verify, (void)                           );
 #endif
 
-
 /* schedule.c */
 _PROTOTYPE( int mthread_getcontext, (ucontext_t *ctxt)                 );
 _PROTOTYPE( void mthread_init_scheduler, (void)                                );
+_PROTOTYPE( void mthread_schedule, (void)                              );
+_PROTOTYPE( void mthread_suspend, (mthread_state_t state)              );
+_PROTOTYPE( void mthread_unsuspend, (mthread_thread_t thread)          );
 
 /* queue.c */
 _PROTOTYPE( void mthread_dump_queue, (mthread_queue_t *queue)          );
+_PROTOTYPE( void mthread_queue_init, (mthread_queue_t *queue)          );
+_PROTOTYPE( void mthread_queue_add, (mthread_queue_t *queue, 
+                                    mthread_thread_t thread)           );
+_PROTOTYPE( mthread_thread_t mthread_queue_remove, (mthread_queue_t *queue));
+_PROTOTYPE( int mthread_queue_isempty, (mthread_queue_t *queue)        );
 
 #endif
index 98f175000cc4b655e7a3a4c338f2af6734574c34..032e19f738143f4f05df034d983c63ebb0a63935 100644 (file)
@@ -1,5 +1,6 @@
 #include <minix/mthread.h>
 #include "global.h"
+#include "proto.h"
 
 /*===========================================================================*
  *                             mthread_queue_add                            *
@@ -12,12 +13,18 @@ mthread_thread_t thread;
  * only one queue at the same time, we can use the threads array's 'next'
  * pointer to point to the next thread on the queue.
  */
+  mthread_tcb_t *last;
+
+  if (!isokthreadid(thread))
+       mthread_panic("Can't append invalid thread ID to a queue");
+
+  last = mthread_find_tcb(thread);
 
   if (mthread_queue_isempty(queue)) {
-       queue->head = queue->tail = thread;
-  } else {
-       threads[queue->tail].m_next = thread;
-       queue->tail = thread; /* 'thread' is the new last in line */
+       queue->head = queue->tail = last;
+  } else  {
+       queue->tail->m_next = last;
+       queue->tail = last;     /* 'last' is the new last in line */
   }
 }
 
@@ -30,7 +37,7 @@ mthread_queue_t *queue;               /* Queue that has to be initialized */
 {
 /* Initialize queue to a known state */
 
-  queue->head = queue->tail = NO_THREAD;
+  queue->head = queue->tail = NULL;
 }
 
 
@@ -40,7 +47,7 @@ mthread_queue_t *queue;               /* Queue that has to be initialized */
 PUBLIC int mthread_queue_isempty(queue)
 mthread_queue_t *queue;
 {
-  return(queue->head == NO_THREAD);
+  return(queue->head == NULL);
 }
 
 
@@ -51,23 +58,28 @@ PUBLIC void mthread_dump_queue(queue)
 mthread_queue_t *queue;
 {
   int threshold, count = 0;
-  mthread_thread_t t;
+  mthread_tcb_t *t;
+  mthread_thread_t tid;
   threshold = no_threads;
 #ifdef MDEBUG
   printf("Dumping queue: ");
 #endif
-  if(queue->head != NO_THREAD) {
+  if(queue->head != NULL) {
        t = queue->head;
+       if (t == &mainthread) tid = MAIN_THREAD;
+       else tid = t->m_tid;
 #ifdef MDEBUG
-       printf("%d ", t);
+       printf("%d ", tid);
 #endif
        count++;
-       t = threads[t].m_next;
-       while (t != NO_THREAD) {
+       t = t->m_next; 
+       while (t != NULL) {
+               if (t == &mainthread) tid = MAIN_THREAD;
+               else tid = t->m_tid;
 #ifdef MDEBUG
-               printf("%d ", t);
+               printf("%d ", tid);
 #endif
-               t = threads[t].m_next;
+               t = t->m_next; 
                count++;
                if (count > threshold) break;
        }
@@ -90,14 +102,25 @@ PUBLIC mthread_thread_t mthread_queue_remove(queue)
 mthread_queue_t *queue;                /* Queue we want a thread from */
 {
 /* Get the first thread in this queue, if there is one. */
-  mthread_thread_t thread = queue->head;
+  mthread_thread_t thread;
+  mthread_tcb_t *tcb;
+
+  /* Calculate thread id from queue head */
+  if (queue->head == NULL) thread = NO_THREAD;
+  else if (queue->head == &mainthread) thread = MAIN_THREAD;
+  else thread = (queue->head->m_tid);
 
   if (thread != NO_THREAD) { /* i.e., this queue is not empty */
-       if (queue->head == queue->tail) /* Queue holds only one thread */
-               queue->head = queue->tail = NO_THREAD; /*So mark thread empty*/
-       else
+       tcb = queue->head;
+       if (queue->head == queue->tail) {
+               /* Queue holds only one thread */
+               queue->head = queue->tail = NULL; /* So mark thread empty */
+       } else {
                /* Second thread in line is the new first */
-               queue->head = threads[thread].m_next;
+               queue->head = queue->head->m_next;
+       }
+
+       tcb->m_next = NULL; /* This thread is no longer part of a queue */
   }
 
   return(thread);
index a1834be1e20a13cc9c7c4e46ed8852bf1dc3e173..96e91beea584484d1f96ff71bd3b2ea652c60116 100644 (file)
@@ -3,9 +3,10 @@
 #include "proto.h"
 
 #define MAIN_CTX       &(mainthread.m_context)
-#define OLD_CTX                &(threads[old_thread].m_context);
-#define CURRENT_CTX    &(threads[current_thread].m_context)
-#define CURRENT_STATE  threads[current_thread].m_state
+#define MAIN_STATE     mainthread.m_state
+#define OLD_CTX                &(threads[old_thread]->m_context)
+#define CURRENT_CTX    &(threads[current_thread]->m_context)
+#define CURRENT_STATE  threads[current_thread]->m_state
 PRIVATE int yield_all;
 
 /*===========================================================================*
@@ -33,10 +34,11 @@ PUBLIC void mthread_schedule(void)
  * first thread off the (FIFO) run queue and resuming that thread. 
  */
 
-  int old_thread;
+  mthread_thread_t old_thread;
+  mthread_tcb_t *new_tcb, *old_tcb;
   ucontext_t *new_ctx, *old_ctx;
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   old_thread = current_thread;
 
@@ -54,24 +56,23 @@ PUBLIC void mthread_schedule(void)
         * thread as there is no work left.
         */
        running_main_thread = 1;
-       current_thread = NO_THREAD;
+       current_thread = MAIN_THREAD;
   } else {
        current_thread = mthread_queue_remove(&run_queue);
        running_main_thread = 0;        /* Running thread after swap */
   }
 
-  if (current_thread == NO_THREAD) 
-       new_ctx = MAIN_CTX;
-  else
-       new_ctx = CURRENT_CTX;
+  /* Find thread entries in tcb... */
+  new_tcb = mthread_find_tcb(current_thread);
+  old_tcb = mthread_find_tcb(old_thread);
 
-  if (old_thread == NO_THREAD) 
-       old_ctx = MAIN_CTX;
-  else
-       old_ctx = OLD_CTX;
+  /* ...and subsequently their contexts */
+  new_ctx = &(new_tcb->m_context);
+  old_ctx = &(old_tcb->m_context);
 
-  if (swapcontext(old_ctx, new_ctx) == -1) 
-       mthread_panic("Could not swap context");
+  if (swapcontext(old_ctx, new_ctx) == -1)
+       mthread_panic("Could not swap context");
+  
 }
 
 
@@ -101,15 +102,18 @@ mthread_state_t state;
  */
 
   int continue_thread = 0;
+  mthread_tcb_t *tcb;
+  ucontext_t *ctx;
 
-  if (state == DEAD) mthread_panic("Shouldn't suspend with DEAD state");
+  if (state == MS_DEAD) mthread_panic("Shouldn't suspend with MS_DEAD state");
+  tcb = mthread_find_tcb(current_thread);
+  tcb->m_state = state;
+  ctx = &(tcb->m_context);
 
-  threads[current_thread].m_state = state;
-  
   /* Save current thread's context */
-  if (mthread_getcontext(CURRENT_CTX) != 0)
+  if (mthread_getcontext(ctx) != 0)
        mthread_panic("Couldn't save current thread's context");
-
+  
   /* We return execution here with setcontext/swapcontext, but also when we
    * simply return from the getcontext call. If continue_thread is non-zero, we
    * are continuing the execution of this thread after a call from setcontext 
@@ -130,9 +134,12 @@ PUBLIC void mthread_unsuspend(thread)
 mthread_thread_t thread; /* Thread to make runnable */
 {
 /* Mark the state of a thread runnable and add it to the run queue */
+  mthread_tcb_t *tcb;
 
-  if (!isokthreadid(thread)) mthread_panic("Invalid thread id\n");
-  threads[thread].m_state = RUNNABLE;
+  if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
+  
+  tcb = mthread_find_tcb(thread);
+  tcb->m_state = MS_RUNNABLE;
   mthread_queue_add(&run_queue, thread);
 }
 
@@ -144,7 +151,7 @@ PUBLIC int mthread_yield(void)
 {
 /* Defer further execution of the current thread and let another thread run. */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (mthread_queue_isempty(&run_queue)) {     /* No point in yielding. */
        return(-1);
@@ -157,9 +164,9 @@ PUBLIC int mthread_yield(void)
   }
 
   mthread_queue_add(&run_queue, current_thread);
-  mthread_suspend(RUNNABLE); /* We're still runnable, but we're just kind
-                             * enough to let someone else run.
-                             */
+  mthread_suspend(MS_RUNNABLE);        /* We're still runnable, but we're just kind
+                                * enough to let someone else run.
+                                */
   return(0);
 }
 
@@ -173,7 +180,7 @@ PUBLIC void mthread_yield_all(void)
  * this function will lead to a deadlock.
  */
 
-  mthread_init();      /* Make sure mthreads is initialized */
+  mthread_init();      /* Make sure libmthread is initialized */
 
   if (yield_all) mthread_panic("Deadlock: two threads trying to yield_all");
   yield_all = 1;
index 04b1ab52438b408d057c501d790c0a4a3f14c25e..1b01638e38029c9b094d3fda13fdb6e46d52db28 100644 (file)
 PUBLIC int errct;
 PRIVATE int count, condition_met;
 PRIVATE int th_a, th_b, th_c, th_d, th_e, th_f, th_g, th_h;
-PRIVATE mutex_t mu[2];
+PRIVATE int mutex_a_step, mutex_b_step, mutex_c_step;
+PRIVATE mutex_t mu[3];
 PRIVATE cond_t condition;
 PRIVATE mutex_t *count_mutex, *condition_mutex;
 PRIVATE once_t once;
+#define VERIFY_MUTEX(a,b,c,esub,eno)   do { \
+       if (mutex_a_step != a) { \
+               printf("Expected %d %d %d, got: %d %d %d\n", \
+                       a, b, c, mutex_a_step, mutex_b_step, mutex_c_step); \
+               err(esub, eno); \
+       } else if (mutex_b_step != b) err(esub, eno); \
+       else if (mutex_c_step != c) err(esub, eno); \
+                                       } while(0)
 #define ROUNDS 14
 #define THRESH1 3
 #define THRESH2 8
@@ -152,13 +161,15 @@ PRIVATE void test_scheduling(void)
   if (mthread_create(&t[2], NULL, thread_a, NULL) != 0) err(1, 3);
   if (mthread_create(&t[3], NULL, thread_d, NULL) != 0) err(1, 4);
   if (mthread_once(&once, thread_e) != 0) err(1, 5);
-  mthread_schedule();
+
+  mthread_yield();
+
   if (mthread_create(&t[4], NULL, thread_c, NULL) != 0) err(1, 6);
-  mthread_schedule();
+  mthread_yield();
   if (mthread_create(&t[5], NULL, thread_b, NULL) != 0) err(1, 7);
   if (mthread_create(&t[6], NULL, thread_a, NULL) != 0) err(1, 8);
-  mthread_schedule();
-  mthread_schedule();
+  mthread_yield();
+  mthread_yield();
   if (mthread_once(&once, thread_e) != 0) err(1, 9);
   if (mthread_once(&once, thread_e) != 0) err(1, 10);
 
@@ -205,7 +216,8 @@ PRIVATE void mutex_a(void *arg)
   mutex_t *mu = (mutex_t *) arg;
   mutex_t mu2;
 
-  if (mthread_mutex_lock(&mu[0]) != 0) err(3, 1);
+  VERIFY_MUTEX(0, 0, 0, 3, 1);
+  if (mthread_mutex_lock(&mu[0]) != 0) err(3, 2);
 
   /* Trying to acquire lock again should fail with EDEADLK */
   if (mthread_mutex_lock(&mu[0]) != -1) err(3, 2);
@@ -225,17 +237,31 @@ PRIVATE void mutex_a(void *arg)
   if (errno != EINVAL) err(3, 7); 
 
   if (mthread_mutex_trylock(&mu[1]) != 0) err(3, 8);
+  mutex_a_step = 1;
   mthread_yield();
-
-  if (mthread_mutex_unlock(&mu[0]) != 0) err(3, 9);
+  VERIFY_MUTEX(1, 0, 0, 3, 9);
+  errno = 0;
+  if (mthread_mutex_trylock(&mu[2]) != -1) err(3, 10);
+  if (errno != EBUSY) err(3, 11);
+  if (mthread_mutex_lock(&mu[2]) != 0) err(3, 12); /* Transfer control to main
+                                                   * loop.
+                                                   */
+  VERIFY_MUTEX(1, 0, 0, 3, 13);
+
+  if (mthread_mutex_unlock(&mu[0]) != 0) err(3, 14);
+  mutex_a_step = 2;
   mthread_yield();
 
-  if (mthread_mutex_unlock(&mu[1]) != 0) err(3, 10);
+  VERIFY_MUTEX(2, 1, 0, 3, 15);
+  if (mthread_mutex_unlock(&mu[1]) != 0) err(3, 16);
+  mutex_a_step = 3;
 
   /* Try with faulty memory locations */
-  if (mthread_mutex_lock(NULL) == 0) err(3, 11);
-  if (mthread_mutex_trylock(NULL) == 0) err(3, 12);
-  if (mthread_mutex_unlock(NULL) == 0) err(3, 13);
+  if (mthread_mutex_lock(NULL) == 0) err(3, 17);
+  if (mthread_mutex_trylock(NULL) == 0) err(3, 18);
+  if (mthread_mutex_unlock(NULL) == 0) err(3, 19);
+
+  if (mthread_mutex_unlock(&mu[2]) != 0) err(3, 20);
 }
 
 
@@ -250,21 +276,29 @@ PRIVATE void mutex_b(void *arg)
    * should not be able to unlock it on behalf of that thread.
    */
 
-  if (mthread_mutex_unlock(&mu[0]) != -1) err(4, 1);
-  if (errno != EPERM) err(4, 2);
+  VERIFY_MUTEX(1, 0, 0, 4, 1);
+  if (mthread_mutex_unlock(&mu[0]) != -1) err(4, 2);
+  if (errno != EPERM) err(4, 3);
 
   /* Probing mu[0] to lock it should tell us it's locked */
-  if (mthread_mutex_trylock(&mu[0]) == 0) err(4, 3);
-  if (errno != EBUSY) err(4, 4);
+  if (mthread_mutex_trylock(&mu[0]) == 0) err(4, 4);
+  if (errno != EBUSY) err(4, 5);
 
   if (mthread_mutex_lock(&mu[0]) != 0) err(4, 5);
+  mutex_b_step = 1;
+  VERIFY_MUTEX(2, 1, 0, 4, 6);
   if (mthread_mutex_lock(&mu[1]) != 0) err(4, 6);
+  mutex_b_step = 2;
+  VERIFY_MUTEX(3, 2, 2, 4, 7);
   mthread_yield();
+  VERIFY_MUTEX(3, 2, 2, 4, 8);
 
   if (mthread_mutex_unlock(&mu[0]) != 0) err(4, 7);
+  mutex_b_step = 3;
   mthread_yield();
 
   if (mthread_mutex_unlock(&mu[1]) != 0) err(4, 8);
+  mutex_b_step = 4;
 }
 
 
@@ -275,14 +309,23 @@ PRIVATE void mutex_c(void *arg)
 {
   mutex_t *mu = (mutex_t *) arg;
 
-  if (mthread_mutex_lock(&mu[1]) != 0) err(5, 1);
+  VERIFY_MUTEX(1, 0, 0, 5, 1);
+  if (mthread_mutex_lock(&mu[1]) != 0) err(5, 2);
+  mutex_c_step = 1;
+  VERIFY_MUTEX(3, 1, 1, 5, 3);
   mthread_yield();
+  VERIFY_MUTEX(3, 1, 1, 5, 4);
 
-  if (mthread_mutex_unlock(&mu[1]) != 0) err(5, 2);
-  if (mthread_mutex_lock(&mu[0]) != 0) err(5, 3);
+  if (mthread_mutex_unlock(&mu[1]) != 0) err(5, 5);
+  mutex_c_step = 2;
+  if (mthread_mutex_lock(&mu[0]) != 0) err(5, 6);
+  mutex_c_step = 3;
+  VERIFY_MUTEX(3, 3, 3, 5, 7);
   mthread_yield();
+  VERIFY_MUTEX(3, 4, 3, 5, 8);
 
-  if (mthread_mutex_unlock(&mu[0]) != 0) err(5, 4);
+  if (mthread_mutex_unlock(&mu[0]) != 0) err(5, 9);
+  mutex_c_step = 4;
 }
 
 
@@ -298,24 +341,33 @@ PRIVATE void test_mutex(void)
 #endif
   if (mthread_mutex_init(&mu[0], NULL) != 0) err(2, 1);
   if (mthread_mutex_init(&mu[1], NULL) != 0) err(2, 2);
+  if (mthread_mutex_init(&mu[2], NULL) != 0) err(2, 3);
 
   if (mthread_create(&t[0], NULL, mutex_a, (void *) mu) != 0) err(2, 3);
   if (mthread_create(&t[1], NULL, mutex_b, (void *) mu) != 0) err(2, 4);
   if (mthread_create(&t[2], NULL, mutex_c, (void *) mu) != 0) err(2, 5);
 
+  if (mthread_mutex_lock(&mu[2]) != 0) err(2, 6);
+
   mthread_yield_all(); /* Should result in a RUNNABLE mutex_a, and a blocked
                        * on mutex mutex_b and mutex_c.
                        */ 
-  mthread_schedule();  /* Should schedule mutex_a to release the locks on its
-                       * mutexes. Consequently allowing mutex_b and mutex_c
+
+  VERIFY_MUTEX(1, 0, 0, 2, 7); /* err(2, 7) */
+  if (mthread_mutex_unlock(&mu[2]) != 0) err(2, 8);
+
+  mthread_yield();     /* Should schedule mutex_a to release the lock on the
+                        * mu[0] mutex. Consequently allowing mutex_b and mutex_c
                        * to acquire locks on the mutexes and exit.
                        */
+  VERIFY_MUTEX(2, 0, 0, 2, 9);
 
   for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++) 
-       if (mthread_join(t[i], NULL) != 0) err(2, 6);
+       if (mthread_join(t[i], NULL) != 0) err(2, 10);
 
-  if (mthread_mutex_destroy(&mu[0]) != 0) err(2, 7);
-  if (mthread_mutex_destroy(&mu[1]) != 0) err(2, 8);
+  if (mthread_mutex_destroy(&mu[0]) != 0) err(2, 11);
+  if (mthread_mutex_destroy(&mu[1]) != 0) err(2, 12);
+  if (mthread_mutex_destroy(&mu[2]) != 0) err(2, 13);
 
 #ifdef MDEBUG
   mthread_verify();
@@ -534,7 +586,7 @@ PRIVATE void test_attributes(void)
   if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 13);
   if (stackaddr != newstackaddr) err(11, 14);
   if (stacksize != newstacksize) err(11, 15);
-
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 16);
   /* Freeing the stack. Note that this is only possible because it wasn't
    * actually used yet by a thread. If it was, mthread would clean it up after
    * usage and this free would do something undefined. */
@@ -544,12 +596,17 @@ PRIVATE void test_attributes(void)
    * values should remain as is.
    */
   newstacksize = MTHREAD_STACK_MIN - 1;
-  if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 16);
+  stackaddr = NULL;
+  stacksize = 0;
+  if (mthread_attr_init(&tattr) != 0) err(11, 17);
+  if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 18);
   if (mthread_attr_setstack(&tattr, newstackaddr, newstacksize) == 0)
-       err(11, 17);
-  if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 18);
-  if (stackaddr == newstackaddr) err(11, 19);
-  if (stacksize == newstacksize) err(11, 20);
+       err(11, 19);
+  if (errno != EINVAL) err(11, 20);
+  if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 21);
+  if (stackaddr == newstackaddr) err(11, 22);
+  if (stacksize == newstacksize) err(11, 23);
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 24);
   /* Again, freeing because we can. Shouldn't do it if it was actually used. */
   free(newstackaddr);
 
@@ -557,69 +614,70 @@ PRIVATE void test_attributes(void)
    * dictate how big that stack should be (2 megabyte, not actually allocated
    * yet).
    */
+  if (mthread_attr_init(&tattr) != 0) err(11, 25);
   if (mthread_attr_setstack(&tattr, NULL /* System allocated */, 2*MEG) != 0)
-       err(11, 21);
-  if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 22);
-  if (stackaddr != NULL) err(11, 23);
-  if (stacksize != 2*MEG) err(11, 24);
+       err(11, 26);
+  if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 27);
+  if (stackaddr != NULL) err(11, 28);
+  if (stacksize != 2*MEG) err(11, 29);
 
   /* Use set/getstacksize to set and retrieve new stack sizes */
   stacksize = 0;
-  if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 25);
-  if (stacksize != 2*MEG) err(11, 26);
+  if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 30);
+  if (stacksize != 2*MEG) err(11, 31);
   newstacksize = MEG;
-  if (mthread_attr_setstacksize(&tattr, newstacksize) != 0) err(11, 27);
-  if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 28);
-  if (stacksize != newstacksize) err(11, 29);
-  if (mthread_attr_destroy(&tattr) != 0) err(11, 30);
+  if (mthread_attr_setstacksize(&tattr, newstacksize) != 0) err(11, 32);
+  if (mthread_attr_getstacksize(&tattr, &stacksize) != 0) err(11, 33);
+  if (stacksize != newstacksize) err(11, 34);
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 35);
 
   /* Perform same tests, but also actually use them in a thread */
-  if (mthread_attr_init(&tattr) != 0) err(11, 31);
+  if (mthread_attr_init(&tattr) != 0) err(11, 36);
   if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
-       err(11, 32);
+       err(11, 37);
   condition_mutex = &mu[0];
-  if (mthread_mutex_init(condition_mutex, NULL) != 0) err(11, 33);
-  if (mthread_cond_init(&condition, NULL) != 0) err(11, 34);
-  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 35);
-  if (mthread_create(&tid, &tattr, thread_f, NULL) != 0) err(11, 36);
+  if (mthread_mutex_init(condition_mutex, NULL) != 0) err(11, 38);
+  if (mthread_cond_init(&condition, NULL) != 0) err(11, 39);
+  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 40);
+  if (mthread_create(&tid, &tattr, thread_f, NULL) != 0) err(11, 41);
   /* Wait for thread_f to finish */
-  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 37);
-  if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 38);
-  if (th_f != 1) err(11, 39);
+  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 42);
+  if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 43);
+  if (th_f != 1) err(11, 44);
   /* Joining a detached thread should fail */
-  if (mthread_join(tid, NULL) == 0) err(11, 40);
-  if (mthread_attr_destroy(&tattr) != 0) err(11, 41);
+  if (mthread_join(tid, NULL) == 0) err(11, 45);
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 46);
 
   /* Try telling the attribute how large the stack should be */
-  if (mthread_attr_init(&tattr) != 0) err(11, 42);
+  if (mthread_attr_init(&tattr) != 0) err(11, 47);
   if (mthread_attr_setstack(&tattr, NULL, 2 * MTHREAD_STACK_MIN) != 0)
-       err(11, 43);
-  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 44);
-  if (mthread_create(&tid, &tattr, thread_g, NULL) != 0) err(11, 45);
+       err(11, 48);
+  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 49);
+  if (mthread_create(&tid, &tattr, thread_g, NULL) != 0) err(11, 50);
   /* Wait for thread_g to finish */
-  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 46);
-  if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 47);
-  if (th_g != 1) err(11, 48);
+  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 51);
+  if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 52);
+  if (th_g != 1) err(11, 53);
   if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
-       err(11, 49); /* Shouldn't affect the join below, as thread is already
+       err(11, 54); /* Shouldn't affect the join below, as thread is already
                      * running as joinable. If this attribute should be 
                      * modified after thread creation, use mthread_detach().
                      */
-  if (mthread_join(tid, NULL) != 0) err(11, 50);
-  if (mthread_attr_destroy(&tattr) != 0) err(11, 51);
+  if (mthread_join(tid, NULL) != 0) err(11, 55);
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 56);
 
   /* Try telling the attribute how large the stack should be and where it is
    * located.
    */
-  if (mthread_attr_init(&tattr) != 0) err(11, 52);
+  if (mthread_attr_init(&tattr) != 0) err(11, 57);
   stacksize = 3 * MEG;
   /* Make sure this test is meaningful. We have to verify that we actually
    * use a custom stack. So we're going to allocate an array on the stack in
    * thread_h that should at least be bigger than the default stack size
    * allocated by the system.
    */
-  if (2 * MEG <= MTHREAD_STACK_MIN) err(11, 53);
-  if ((stackaddr = malloc(stacksize)) == NULL) err(11, 54);
+  if (2 * MEG <= MTHREAD_STACK_MIN) err(11, 58);
+  if ((stackaddr = malloc(stacksize)) == NULL) err(11, 59);
   /* Fill stack with pattern. We assume that the beginning of the stack
    * should be overwritten with something and that the end should remain
    * untouched. The thread will zero-fill around two-thirds of the stack with
@@ -629,33 +687,34 @@ PRIVATE void test_attributes(void)
   no_ints = stacksize / sizeof(int);
   for (i = 0; i < no_ints ; i++) 
        stackp[i] = MAGIC;
-  if (mthread_attr_setstack(&tattr, stackaddr, stacksize) != 0) err(11, 55);
-  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 56);
+  if (mthread_attr_setstack(&tattr, stackaddr, stacksize) != 0) err(11, 60);
+  if (mthread_mutex_lock(condition_mutex) != 0) err(11, 61);
   if (mthread_create(&tid, &tattr, thread_h, (void *) &stacksize) != 0)        
-       err(11, 57);
+       err(11, 62);
   /* Wait for thread h to finish */
-  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 58);
-  if (th_h != 1) err(11, 59);
+  if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 63);
+  if (th_h != 1) err(11, 64);
+  if (mthread_mutex_unlock(condition_mutex) != 0) err(11, 65);
 
   /* Verify stack hypothesis; we assume a stack is used from the top and grows
    * downwards. At this point the stack should still exist, because we haven't
    * 'joined' yet. After joining, the stack is cleaned up and this test becomes
    * useless. */
 #if (_MINIX_CHIP == _CHIP_INTEL)
-  if (stackp[0] != MAGIC) err(11, 60); /* End of the stack */
+  if (stackp[0] != MAGIC) err(11, 66); /* End of the stack */
   for (i = no_ints - 1 - 16; i < no_ints; i++)
        if (stackp[i] != MAGIC) stack_untouched = 0;
-  if (stack_untouched) err(11, 61); /* Beginning of the stack */
-  if (stackp[no_ints / 2] != 0) err(11, 62);/*Zero half way through the stack*/
+  if (stack_untouched) err(11, 67); /* Beginning of the stack */
+  if (stackp[no_ints / 2] != 0) err(11, 68);/*Zero half way through the stack*/
 #else
 #error "Unsupported chip for this test"
 #endif
 
-  if (mthread_join(tid, (void *) &status) != 0) err(11, 63);
-  if (status != stacksize) err(11, 64);
-  if (mthread_attr_destroy(&tattr) != 0) err(11, 65); 
-  if (mthread_mutex_destroy(condition_mutex) != 0) err(11, 66);
-  if (mthread_cond_destroy(&condition) != 0) err(11, 67);
+  if (mthread_join(tid, (void *) &status) != 0) err(11, 69);
+  if (status != stacksize) err(11, 70);
+  if (mthread_attr_destroy(&tattr) != 0) err(11, 71); 
+  if (mthread_mutex_destroy(condition_mutex) != 0) err(11, 72);
+  if (mthread_cond_destroy(&condition) != 0) err(11, 73);
 
 #ifdef MDEBUG
   mthread_verify();
@@ -669,6 +728,7 @@ int main(void)
 {
   errct = 0;
   th_a = th_b = th_c = th_d = th_e = th_f = th_g = th_h = 0;
+  mutex_a_step = mutex_b_step = mutex_c_step = 0;
   once = MTHREAD_ONCE_INIT;
 
   start(59);