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;
};
};
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;
};
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
_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
/* 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);
}
{
/* 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
*===========================================================================*/
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 */
/* 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);
}
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;
}
#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);
}
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
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;
{
/* 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;
} 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");
/* Thread has exited; copy results */
if(value != NULL)
- *value = threads[join].m_result;
+ *value = tcb->m_result;
/* Deallocate resources */
mthread_thread_stop(join);
{
/* 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;
{
/* 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);
}
* 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 */
}
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;
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;
}
{
/* 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);
}
#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) );
#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 *
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;
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);
}
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;
}
/* 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);
}
/* 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;
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;
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;
}
}
{
/* 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);
}
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;
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);
#include <assert.h>
-#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];
#include <minix/mthread.h>
#include <stdio.h>
-#include "proto.h"
#include "global.h"
+#include "proto.h"
/*===========================================================================*
* mthread_debug_f *
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);
}
* 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();
*===========================================================================*/
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");
}
}
#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) );
{
/* Invalidate mutex and deallocate resources. */
- int i;
+ mthread_thread_t t;
+ mthread_tcb_t *tcb;
mthread_init(); /* Make sure mthreads is initialized */
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);
}
/*===========================================================================*
* 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. */
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. */
loopitem = vm_front;
while (loopitem != NULL) {
+ printf("mutex corruption: owner: %d\n", loopitem->owner);
loopitem = loopitem->next;
r = 0;
}
#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
_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
#include <minix/mthread.h>
#include "global.h"
+#include "proto.h"
/*===========================================================================*
* mthread_queue_add *
* 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 */
}
}
{
/* Initialize queue to a known state */
- queue->head = queue->tail = NO_THREAD;
+ queue->head = queue->tail = NULL;
}
PUBLIC int mthread_queue_isempty(queue)
mthread_queue_t *queue;
{
- return(queue->head == NO_THREAD);
+ return(queue->head == NULL);
}
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;
}
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);
#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;
/*===========================================================================*
* 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;
* 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");
+
}
*/
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
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);
}
{
/* 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);
}
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);
}
* 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;
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
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);
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);
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);
}
* 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;
}
{
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;
}
#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();
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. */
* 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);
* 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
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();
{
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);