diff --git a/include/minix/mthread.h b/include/minix/mthread.h index e98cd8d21..7549515d4 100644 --- a/include/minix/mthread.h +++ b/include/minix/mthread.h @@ -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 diff --git a/lib/libmthread/allocate.c b/lib/libmthread/allocate.c index 2c769310e..29bb188ca 100644 --- a/lib/libmthread/allocate.c +++ b/lib/libmthread/allocate.c @@ -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; - threads[current_thread].m_state = FALLBACK_EXITING; + tcb = mthread_find_tcb(current_thread); + + 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); } diff --git a/lib/libmthread/attribute.c b/lib/libmthread/attribute.c index b69664078..dda0aaf3d 100644 --- a/lib/libmthread/attribute.c +++ b/lib/libmthread/attribute.c @@ -1,6 +1,6 @@ #include -#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) ); diff --git a/lib/libmthread/condition.c b/lib/libmthread/condition.c index e86380fcf..bb8a6e341 100644 --- a/lib/libmthread/condition.c +++ b/lib/libmthread/condition.c @@ -1,12 +1,12 @@ #include -#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); diff --git a/lib/libmthread/global.h b/lib/libmthread/global.h index 0dc58716b..195622f50 100644 --- a/lib/libmthread/global.h +++ b/lib/libmthread/global.h @@ -6,20 +6,42 @@ #include -#define NO_THREADS 3 -#define MAX_THREAD_POOL 1000 +#define NO_THREADS 4 +#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]; diff --git a/lib/libmthread/misc.c b/lib/libmthread/misc.c index 230bd8bb8..81301f31b 100644 --- a/lib/libmthread/misc.c +++ b/lib/libmthread/misc.c @@ -1,7 +1,7 @@ #include #include -#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"); } } diff --git a/lib/libmthread/mutex.c b/lib/libmthread/mutex.c index 059744056..06d1afab6 100644 --- a/lib/libmthread/mutex.c +++ b/lib/libmthread/mutex.c @@ -1,6 +1,6 @@ #include -#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; } diff --git a/lib/libmthread/proto.h b/lib/libmthread/proto.h index 3d77a6590..7ef4aa27b 100644 --- a/lib/libmthread/proto.h +++ b/lib/libmthread/proto.h @@ -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 diff --git a/lib/libmthread/queue.c b/lib/libmthread/queue.c index 98f175000..032e19f73 100644 --- a/lib/libmthread/queue.c +++ b/lib/libmthread/queue.c @@ -1,5 +1,6 @@ #include #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); diff --git a/lib/libmthread/scheduler.c b/lib/libmthread/scheduler.c index a1834be1e..96e91beea 100644 --- a/lib/libmthread/scheduler.c +++ b/lib/libmthread/scheduler.c @@ -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; diff --git a/test/test59.c b/test/test59.c index 04b1ab524..1b01638e3 100644 --- a/test/test59.c +++ b/test/test59.c @@ -16,10 +16,19 @@ 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(); + 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[0]) != 0) err(3, 9); - 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);