Add libmthread and test59 to test the implementation

This commit is contained in:
Thomas Veerman 2010-09-21 12:22:38 +00:00
parent 3cc13b29f0
commit 12e167f672
16 changed files with 2725 additions and 4 deletions

View file

@ -19,7 +19,7 @@ INCS+= minix/a.out.h minix/bitmap.h minix/callnr.h minix/cdrom.h \
minix/acpi.h \
minix/drivers.h minix/drvlib.h minix/ds.h minix/endpoint.h \
minix/fslib.h minix/ioctl.h minix/ipc.h minix/ipcconst.h \
minix/keymap.h minix/minlib.h minix/mq.h \
minix/keymap.h minix/minlib.h minix/mq.h minix/mthread.h \
minix/netdriver.h minix/partition.h minix/paths.h \
minix/portio.h minix/priv.h minix/procfs.h minix/profile.h \
minix/queryparam.h \

143
include/minix/mthread.h Normal file
View file

@ -0,0 +1,143 @@
#ifndef _MTHREAD_H
#define _MTHREAD_H
#define _SYSTEM
#include <minix/config.h> /* MUST be first */
#include <ansi.h> /* MUST be second */
#include <minix/const.h>
#include <sys/types.h>
#include <stdio.h>
#include <ucontext.h>
#include <errno.h>
#include <stdlib.h>
#include <alloca.h>
#include <limits.h>
typedef int mthread_thread_t;
typedef int mthread_once_t;
typedef void * mthread_condattr_t;
typedef void * mthread_mutexattr_t;
typedef struct {
mthread_thread_t head;
mthread_thread_t 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 */
struct __mthread_mutex *prev;
struct __mthread_mutex *next;
};
typedef struct __mthread_mutex *mthread_mutex_t;
struct __mthread_cond {
struct __mthread_mutex *mutex; /* Associate mutex with condition */
struct __mthread_cond *prev;
struct __mthread_cond *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;
int a_detachstate;
struct __mthread_attr *prev;
struct __mthread_attr *next;
};
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
#define MTHREAD_STACK_MIN MINSIGSTKSZ
/* allocate.c */
_PROTOTYPE( int mthread_create, (mthread_thread_t *thread,
mthread_attr_t *tattr,
void (*proc)(void *), void *arg) );
_PROTOTYPE( int mthread_detach, (mthread_thread_t thread) );
_PROTOTYPE( int mthread_equal, (mthread_thread_t l, mthread_thread_t r) );
_PROTOTYPE( void mthread_exit, (void *value) );
_PROTOTYPE( int mthread_join, (mthread_thread_t thread, void **value) );
_PROTOTYPE( int mthread_once, (mthread_once_t *once,
void (*proc)(void)) );
_PROTOTYPE( mthread_thread_t mthread_self, (void) );
/* attribute.c */
_PROTOTYPE( int mthread_attr_destroy, (mthread_attr_t *tattr) );
_PROTOTYPE( int mthread_attr_getdetachstate, (mthread_attr_t *tattr,
int *detachstate) );
_PROTOTYPE( int mthread_attr_getstack, (mthread_attr_t *tattr,
void **stackaddr,
size_t *stacksize) );
_PROTOTYPE( int mthread_attr_getstacksize, (mthread_attr_t *tattr,
size_t *stacksize) );
_PROTOTYPE( int mthread_attr_init, (mthread_attr_t *tattr) );
_PROTOTYPE( int mthread_attr_setdetachstate, (mthread_attr_t *tattr,
int detachstate) );
_PROTOTYPE( int mthread_attr_setstack, (mthread_attr_t *tattr,
void *stackaddr,
size_t stacksize) );
_PROTOTYPE( int mthread_attr_setstacksize, (mthread_attr_t *tattr,
size_t stacksize) );
/* condition.c */
_PROTOTYPE( int mthread_cond_broadcast, (mthread_cond_t *cond) );
_PROTOTYPE( int mthread_cond_destroy, (mthread_cond_t *cond) );
_PROTOTYPE( int mthread_cond_init, (mthread_cond_t *cond,
mthread_condattr_t *cattr) );
_PROTOTYPE( int mthread_cond_signal, (mthread_cond_t *cond) );
_PROTOTYPE( int mthread_cond_wait, (mthread_cond_t *cond,
mthread_mutex_t *mutex) );
/* misc.c */
_PROTOTYPE( void mthread_stats, (void) );
_PROTOTYPE( void mthread_verify_f, (char *f, int l) );
#define mthread_verify() mthread_verify_f(__FILE__, __LINE__)
/* mutex.c */
_PROTOTYPE( int mthread_mutex_destroy, (mthread_mutex_t *mutex) );
_PROTOTYPE( int mthread_mutex_init, (mthread_mutex_t *mutex,
mthread_mutexattr_t *mattr) );
_PROTOTYPE( int mthread_mutex_lock, (mthread_mutex_t *mutex) );
_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

View file

@ -2,7 +2,7 @@
SUBDIR= csu libc libcurses libdriver libnetdriver libend libedit libm libsys \
libtimers libutil libbz2 libl libhgfs libz libfetch libarchive \
libvtreefs libaudiodriver
libvtreefs libaudiodriver libmthread
.if ${COMPILER_TYPE} == "ack"
SUBDIR+= ack/libd ack/libe ack/libfp ack/liby

16
lib/libmthread/Makefile Normal file
View file

@ -0,0 +1,16 @@
# Makefile for libmthread
CPPFLAGS+=-O -D_MINIX -D_POSIX_SOURCE -Wall
LIB= mthread
SRCS= \
allocate.c \
attribute.c \
mutex.c \
misc.c \
queue.c \
condition.c \
scheduler.c
.include <bsd.lib.mk>

500
lib/libmthread/allocate.c Normal file
View file

@ -0,0 +1,500 @@
#define ALLOCATE
#include <errno.h>
#include <minix/mthread.h>
#include "global.h"
#include "proto.h"
#define FALLBACK_CTX (&(fallback.m_context))
FORWARD _PROTOTYPE( void mthread_fallback, (void) );
FORWARD _PROTOTYPE( int mthread_increase_thread_pool, (void) );
FORWARD _PROTOTYPE( void mthread_thread_init, (mthread_thread_t thread,
mthread_attr_t *tattr,
void (*proc)(void *),
void *arg) );
FORWARD _PROTOTYPE( void mthread_thread_reset, (mthread_thread_t thread));
FORWARD _PROTOTYPE( void mthread_thread_stop, (mthread_thread_t thread));
FORWARD _PROTOTYPE( void mthread_trampoline, (void) );
PRIVATE int initialized = 0;
PRIVATE struct __mthread_attr default_attr = { MTHREAD_STACK_MIN,
NULL,
MTHREAD_CREATE_JOINABLE,
NULL, NULL };
/*===========================================================================*
* mthread_equal *
*===========================================================================*/
PUBLIC int mthread_equal(l, r)
mthread_thread_t l;
mthread_thread_t r;
{
/* Compare two thread ids */
mthread_init(); /* Make sure mthreads is initialized */
return(l == r);
}
/*===========================================================================*
* mthread_create *
*===========================================================================*/
PUBLIC int mthread_create(threadid, tattr, proc, arg)
mthread_thread_t *threadid;
mthread_attr_t *tattr;
void (*proc)(void *);
void *arg;
{
/* Register procedure proc for execution in a thread. */
mthread_thread_t thread;
mthread_init(); /* Make sure mthreads is initialized */
if (proc == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_queue_isempty(&free_threads)) {
thread = mthread_queue_remove(&free_threads);
mthread_thread_init(thread, tattr, proc, arg);
used_threads++;
if(threadid != NULL)
*threadid = (mthread_thread_t) thread;
#ifdef MDEBUG
printf("Inited thread %d\n", thread);
#endif
return(0);
} else {
if (mthread_increase_thread_pool() == -1) {
errno = EAGAIN;
return(-1);
}
return mthread_create(threadid, tattr, proc, arg);
}
}
/*===========================================================================*
* mthread_detach *
*===========================================================================*/
PUBLIC int mthread_detach(detach)
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 */
if (!isokthreadid(detach)) {
errno = ESRCH;
return(-1);
} else if (threads[detach].m_state == DEAD) {
errno = ESRCH;
return(-1);
} else if (threads[detach].m_attr.a_detachstate != MTHREAD_CREATE_DETACHED) {
if (threads[detach].m_state == EXITING) {
mthread_thread_stop(detach);
} else {
threads[detach].m_attr.a_detachstate = MTHREAD_CREATE_DETACHED;
}
}
return(0);
}
/*===========================================================================*
* mthread_exit *
*===========================================================================*/
PUBLIC void mthread_exit(value)
void *value;
{
/* Make a thread stop running and store the result value. */
int fallback_exit = 0;
mthread_thread_t stop;
mthread_init(); /* Make sure mthreads is initialized */
stop = current_thread;
if (threads[stop].m_state == 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)
fallback_exit = 1;
threads[stop].m_result = value;
threads[stop].m_state = EXITING;
if (threads[stop].m_attr.a_detachstate == MTHREAD_CREATE_DETACHED) {
mthread_thread_stop(stop);
} else {
/* Joinable thread; notify possibly waiting thread */
if (mthread_cond_signal(&(threads[stop].m_exited)) != 0)
mthread_panic("Couldn't signal exit");
/* The thread that's actually doing the join will eventually clean
* up this thread (i.e., call mthread_thread_stop).
*/
}
/* The fallback thread does a mthread_schedule. If we're not running from
* that thread, we have to do it ourselves.
*/
if (!fallback_exit)
mthread_schedule();
}
/*===========================================================================*
* mthread_fallback *
*===========================================================================*/
PRIVATE void mthread_fallback(void)
{
/* The mthreads 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.
*/
threads[current_thread].m_state = FALLBACK_EXITING;
mthread_exit(NULL);
/* Reconstruct fallback context for next invocation */
makecontext(FALLBACK_CTX, (void (*) (void)) mthread_fallback, 0);
/* Let another thread run */
mthread_schedule();
}
/*===========================================================================*
* mthread_increase_thread_pool *
*===========================================================================*/
PRIVATE int mthread_increase_thread_pool(void)
{
/* Increase thread pool. No fancy algorithms, just double the size. */
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 (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));
if (new_tcb == NULL) {
mthread_debug("Can't increase thread pool");
return(-1);
}
threads = new_tcb;
no_threads = new_no_threads;
/* Add newly available threads to free_threads */
for (i = old_no_threads; i < new_no_threads; i++) {
mthread_queue_add(&free_threads, i);
mthread_thread_reset(i);
}
#ifdef MDEBUG
printf("Increased thread pool from %d to %d threads\n", no_threads, new_no_threads);
#endif
return(0);
}
/*===========================================================================*
* mthread_init *
*===========================================================================*/
PUBLIC void mthread_init(void)
{
/* Initialize thread system; allocate thread structures and start creating
* threads.
*/
if (!initialized) {
int i;
no_threads = NO_THREADS;
used_threads = 0;
running_main_thread = 1;/* mthread_init can only be called from the
* main thread. Calling it from a thread will
* not enter this clause.
*/
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");
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_size = STACKSZ;
if (FALLBACK_CTX->uc_stack.ss_sp == NULL)
mthread_panic("Could not allocate stack space to fallback "
"thread");
makecontext(FALLBACK_CTX, (void (*) (void)) mthread_fallback, 0);
initialized = 1;
}
}
/*===========================================================================*
* mthread_join *
*===========================================================================*/
PUBLIC int mthread_join(join, value)
mthread_thread_t join;
void **value;
{
/* Wait for a thread to stop running and copy the result. */
mthread_init(); /* Make sure mthreads is initialized */
if (!isokthreadid(join)) {
errno = ESRCH;
return(-1);
} else if (join == current_thread) {
errno = EDEADLK;
return(-1);
} else if (threads[join].m_state == DEAD) {
errno = ESRCH;
return(-1);
} else if (threads[join].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) {
mthread_cond_t *c;
mthread_mutex_t *m;
c = &(threads[join].m_exited);
m = &(threads[join].m_exitm);
if (mthread_mutex_init(m, NULL) != 0)
mthread_panic("Couldn't initialize mutex to join\n");
if (mthread_mutex_lock(m) != 0)
mthread_panic("Couldn't lock mutex to join\n");
if (mthread_cond_wait(c, m) != 0)
mthread_panic("Couldn't wait for join condition\n");
if (mthread_mutex_unlock(m) != 0)
mthread_panic("Couldn't unlock mutex to join\n");
if (mthread_mutex_destroy(m) != 0)
mthread_panic("Couldn't destroy mutex to join\n");
}
/* Thread has exited; copy results */
if(value != NULL)
*value = threads[join].m_result;
/* Deallocate resources */
mthread_thread_stop(join);
return(0);
}
/*===========================================================================*
* mthread_once *
*===========================================================================*/
PUBLIC int mthread_once(once, proc)
mthread_once_t *once;
void (*proc)(void);
{
/* Run procedure proc just once */
mthread_init(); /* Make sure mthreads is initialized */
if (once == NULL || proc == NULL) {
errno = EINVAL;
return(-1);
}
if (*once != 1) proc();
*once = 1;
return(0);
}
/*===========================================================================*
* mthread_self *
*===========================================================================*/
PUBLIC mthread_thread_t mthread_self(void)
{
/* Return the thread id of the thread calling this function. */
mthread_init(); /* Make sure mthreads is initialized */
return(current_thread);
}
/*===========================================================================*
* mthread_thread_init *
*===========================================================================*/
PRIVATE void mthread_thread_init(thread, tattr, proc, arg)
mthread_thread_t thread;
mthread_attr_t *tattr;
void (*proc)(void *);
void *arg;
{
/* Initialize a thread so that it, when unsuspended, will run the given
* procedure with the given parameter. The thread is marked as runnable.
*/
#define THIS_CTX (&(threads[thread].m_context))
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;
/* 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);
else {
threads[thread].m_attr = default_attr;
}
if (mthread_cond_init(&(threads[thread].m_exited), NULL) != 0)
mthread_panic("Could not initialize thread");
/* First set the fallback thread, */
THIS_CTX->uc_link = FALLBACK_CTX;
/* then construct this thread's context to run procedure proc. */
if (getcontext(THIS_CTX) == -1)
mthread_panic("Failed to initialize context state");
stacksize = threads[thread].m_attr.a_stacksize;
stackaddr = threads[thread].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)
mthread_panic("Failed to allocate stack to thread");
} else
THIS_CTX->uc_stack.ss_sp = stackaddr;
THIS_CTX->uc_stack.ss_size = stacksize;
makecontext(THIS_CTX, mthread_trampoline, 0);
mthread_unsuspend(thread); /* Make thread runnable */
}
/*===========================================================================*
* mthread_thread_reset *
*===========================================================================*/
PRIVATE void mthread_thread_reset(thread)
mthread_thread_t thread;
{
/* Reset the thread to its default values. Free the allocated stack space. */
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->m_proc = NULL;
rt->m_arg = NULL;
rt->m_result = NULL;
rt->m_cond = NULL;
if (rt->m_context.uc_stack.ss_sp)
free(rt->m_context.uc_stack.ss_sp); /* Free allocated stack */
rt->m_context.uc_stack.ss_sp = NULL;
rt->m_context.uc_stack.ss_size = 0;
rt->m_context.uc_link = NULL;
}
/*===========================================================================*
* mthread_thread_stop *
*===========================================================================*/
PRIVATE void mthread_thread_stop(thread)
mthread_thread_t thread;
{
/* Stop thread from running. Deallocate resources. */
mthread_tcb_t *stop_thread;
if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
stop_thread = &(threads[thread]);
if (stop_thread->m_state == DEAD) {
/* Already DEAD, nothing to do */
return;
}
mthread_thread_reset(thread);
if (mthread_cond_destroy(&(stop_thread->m_exited)) != 0)
mthread_panic("Could not destroy condition at thread deallocation\n");
used_threads--;
mthread_queue_add(&free_threads, thread);
}
/*===========================================================================*
* mthread_trampoline *
*===========================================================================*/
PRIVATE void mthread_trampoline(void)
{
/* Execute the /current_thread's/ procedure. Store its result. */
void *r;
r = (threads[current_thread].m_proc)(threads[current_thread].m_arg);
mthread_exit(r);
}

354
lib/libmthread/attribute.c Normal file
View file

@ -0,0 +1,354 @@
#include <minix/mthread.h>
#include "proto.h"
#include "global.h"
PRIVATE struct __mthread_attr *va_front, *va_rear;
FORWARD _PROTOTYPE( void mthread_attr_add, (mthread_attr_t *a) );
FORWARD _PROTOTYPE( void mthread_attr_remove, (mthread_attr_t *a) );
FORWARD _PROTOTYPE( int mthread_attr_valid, (mthread_attr_t *a) );
/*===========================================================================*
* mthread_init_valid_attributes *
*===========================================================================*/
PUBLIC void mthread_init_valid_attributes(void)
{
/* Initialize list of valid attributs */
va_front = va_rear = NULL;
}
/*===========================================================================*
* mthread_attr_add *
*===========================================================================*/
PRIVATE void mthread_attr_add(a)
mthread_attr_t *a;
{
/* Add attribute to list of valid, initialized attributes */
if (va_front == NULL) { /* Empty list */
va_front = *a;
(*a)->prev = NULL;
} else {
va_rear->next = *a;
(*a)->prev = va_rear;
}
(*a)->next = NULL;
va_rear = *a;
}
/*===========================================================================*
* mthread_attr_destroy *
*===========================================================================*/
PUBLIC int mthread_attr_destroy(attr)
mthread_attr_t *attr;
{
/* Invalidate attribute and deallocate resources. */
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_attr_valid(attr)) {
errno = EINVAL;
return(-1);
}
/* Valide attribute; invalidate it */
mthread_attr_remove(attr);
free(*attr);
*attr = NULL;
return(0);
}
/*===========================================================================*
* mthread_attr_init *
*===========================================================================*/
PUBLIC int mthread_attr_init(attr)
mthread_attr_t *attr; /* Attribute */
{
/* Initialize the attribute to a known state. */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EAGAIN;
return(-1);
} else if (mthread_attr_valid(attr)) {
errno = EBUSY;
return(-1);
}
if ((a = malloc(sizeof(struct __mthread_attr))) == NULL)
return(-1);
a->a_detachstate = MTHREAD_CREATE_JOINABLE;
a->a_stackaddr = NULL;
a->a_stacksize = (size_t) 0;
*attr = (mthread_attr_t) a;
mthread_attr_add(attr); /* Validate attribute: attribute now in use */
return(0);
}
/*===========================================================================*
* mthread_attr_getdetachstate *
*===========================================================================*/
PUBLIC int mthread_attr_getdetachstate(attr, detachstate)
mthread_attr_t *attr;
int *detachstate;
{
/* Get detachstate of a thread attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr)) {
errno = EINVAL;
return(-1);
}
*detachstate = a->a_detachstate;
return(0);
}
/*===========================================================================*
* mthread_attr_setdetachstate *
*===========================================================================*/
PUBLIC int mthread_attr_setdetachstate(attr, detachstate)
mthread_attr_t *attr;
int detachstate;
{
/* Set detachstate of a thread attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr)) {
errno = EINVAL;
return(-1);
} else if(detachstate != MTHREAD_CREATE_JOINABLE &&
detachstate != MTHREAD_CREATE_DETACHED) {
errno = EINVAL;
return(-1);
}
a->a_detachstate = detachstate;
return(0);
}
/*===========================================================================*
* mthread_attr_getstack *
*===========================================================================*/
PUBLIC int mthread_attr_getstack(attr, stackaddr, stacksize)
mthread_attr_t *attr;
void **stackaddr;
size_t *stacksize;
{
/* Get stack attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr)) {
errno = EINVAL;
return(-1);
}
*stackaddr = a->a_stackaddr;
*stacksize = a->a_stacksize;
return(0);
}
/*===========================================================================*
* mthread_attr_getstacksize *
*===========================================================================*/
PUBLIC int mthread_attr_getstacksize(attr, stacksize)
mthread_attr_t *attr;
size_t *stacksize;
{
/* Get stack size attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr)) {
errno = EINVAL;
return(-1);
}
*stacksize = a->a_stacksize;
return(0);
}
/*===========================================================================*
* mthread_attr_setstack *
*===========================================================================*/
PUBLIC int mthread_attr_setstack(attr, stackaddr, stacksize)
mthread_attr_t *attr;
void *stackaddr;
size_t stacksize;
{
/* Set stack attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr) || stacksize < MTHREAD_STACK_MIN) {
errno = EINVAL;
return(-1);
}
/* We don't care about address alignment (POSIX standard). The ucontext
* system calls will make sure that the provided stack will be aligned (at
* the cost of some memory if needed).
*/
a->a_stackaddr = stackaddr;
a->a_stacksize = stacksize;
return(0);
}
/*===========================================================================*
* mthread_attr_setstacksize *
*===========================================================================*/
PUBLIC int mthread_attr_setstacksize(attr, stacksize)
mthread_attr_t *attr;
size_t stacksize;
{
/* Set stack size attribute */
struct __mthread_attr *a;
mthread_init(); /* Make sure mthreads is initialized */
if (attr == NULL) {
errno = EINVAL;
return(-1);
}
a = (struct __mthread_attr *) *attr;
if (!mthread_attr_valid(attr) || stacksize < MTHREAD_STACK_MIN) {
errno = EINVAL;
return(-1);
}
a->a_stacksize = stacksize;
return(0);
}
/*===========================================================================*
* mthread_attr_remove *
*===========================================================================*/
PRIVATE void mthread_attr_remove(a)
mthread_attr_t *a;
{
/* Remove attribute from list of valid, initialized attributes */
if ((*a)->prev == NULL)
va_front = (*a)->next;
else
(*a)->prev->next = (*a)->next;
if ((*a)->next == NULL)
va_rear = (*a)->prev;
else
(*a)->next->prev = (*a)->prev;
}
/*===========================================================================*
* mthread_attr_valid *
*===========================================================================*/
PRIVATE int mthread_attr_valid(a)
mthread_attr_t *a;
{
/* Check to see if attribute is on the list of valid attributes */
struct __mthread_attr *loopitem;
mthread_init(); /* Make sure mthreads is initialized */
loopitem = va_front;
while (loopitem != NULL) {
if (loopitem == *a)
return(1);
loopitem = loopitem->next;
}
return(0);
}
/*===========================================================================*
* mthread_attr_verify *
*===========================================================================*/
#ifdef MDEBUG
PUBLIC int mthread_attr_verify(void)
{
/* Return true when no attributes are in use */
struct __mthread_attr *loopitem;
mthread_init(); /* Make sure mthreads is initialized */
loopitem = va_front;
while (loopitem != NULL) {
loopitem = loopitem->next;
return(0);
}
return(1);
}
#endif

279
lib/libmthread/condition.c Normal file
View file

@ -0,0 +1,279 @@
#include <minix/mthread.h>
#include "proto.h"
#include "global.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) );
/*===========================================================================*
* mthread_init_valid_conditions *
*===========================================================================*/
PUBLIC void mthread_init_valid_conditions(void)
{
/* Initialize condition variable list */
vc_front = vc_rear = NULL;
}
/*===========================================================================*
* mthread_cond_add *
*===========================================================================*/
PRIVATE void mthread_cond_add(c)
mthread_cond_t *c;
{
/* Add condition to list of valid, initialized conditions */
if (vc_front == NULL) { /* Empty list */
vc_front = *c;
(*c)->prev = NULL;
} else {
vc_rear->next = *c;
(*c)->prev = vc_rear;
}
(*c)->next = NULL;
vc_rear = *c;
}
/*===========================================================================*
* mthread_cond_broadcast *
*===========================================================================*/
PUBLIC int mthread_cond_broadcast(cond)
mthread_cond_t *cond;
{
/* Signal all threads waiting for condition 'cond'. */
int i;
mthread_init(); /* Make sure mthreads is initialized */
if(cond == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_cond_valid(cond)) {
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);
return(0);
}
/*===========================================================================*
* mthread_cond_destroy *
*===========================================================================*/
PUBLIC int mthread_cond_destroy(cond)
mthread_cond_t *cond;
{
/* Destroy a condition variable. Make sure it's not in use */
int i;
mthread_init(); /* Make sure mthreads is initialized */
if (cond == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_cond_valid(cond)) {
errno = EINVAL;
return(-1);
}
/* 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) {
errno = EBUSY;
return(-1);
}
}
/* Not in use; invalidate it. */
mthread_cond_remove(cond);
free(*cond);
*cond = NULL;
return(0);
}
/*===========================================================================*
* mthread_cond_init *
*===========================================================================*/
PUBLIC int mthread_cond_init(cond, cattr)
mthread_cond_t *cond;
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 */
if (cond == NULL) {
errno = EINVAL;
return(-1);
} else if (cattr != NULL) {
errno = ENOSYS;
return(-1);
}
if (mthread_cond_valid(cond)) {
/* Already initialized */
errno = EBUSY;
return(-1);
}
if ((c = malloc(sizeof(struct __mthread_cond))) == NULL)
return(-1);
c->mutex = NULL;
*cond = (mthread_cond_t) c;
mthread_cond_add(cond);
return(0);
}
/*===========================================================================*
* mthread_cond_remove *
*===========================================================================*/
PRIVATE void mthread_cond_remove(c)
mthread_cond_t *c;
{
/* Remove condition from list of valid, initialized conditions */
if ((*c)->prev == NULL)
vc_front = (*c)->next;
else
(*c)->prev->next = (*c)->next;
if ((*c)->next == NULL)
vc_rear = (*c)->prev;
else
(*c)->next->prev = (*c)->prev;
}
/*===========================================================================*
* mthread_cond_signal *
*===========================================================================*/
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_init(); /* Make sure mthreads is initialized */
if(cond == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_cond_valid(cond)) {
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);
break;
}
}
return(0);
}
/*===========================================================================*
* mthread_cond_valid *
*===========================================================================*/
PRIVATE int mthread_cond_valid(c)
mthread_cond_t *c;
{
/* Check to see if cond is on the list of valid conditions */
struct __mthread_cond *loopitem;
loopitem = vc_front;
while (loopitem != NULL) {
if (loopitem == *c)
return(1);
loopitem = loopitem->next;
}
return(0);
}
/*===========================================================================*
* mthread_cond_verify *
*===========================================================================*/
#ifdef MDEBUG
PUBLIC int mthread_cond_verify(void)
{
/* Return true in case no condition variables are in use. */
mthread_init(); /* Make sure mthreads is initialized */
return(vc_front == NULL);
}
#endif
/*===========================================================================*
* mthread_cond_wait *
*===========================================================================*/
PUBLIC int mthread_cond_wait(cond, mutex)
mthread_cond_t *cond;
mthread_mutex_t *mutex;
{
/* Wait for a condition to be signaled */
struct __mthread_cond *c;
struct __mthread_mutex *m;
mthread_init(); /* Make sure mthreads is initialized */
if (cond == NULL || mutex == NULL) {
errno = EINVAL;
return(-1);
}
c = (struct __mthread_cond *) *cond;
m = (struct __mthread_mutex *) *mutex;
if (!mthread_cond_valid(cond) || !mthread_mutex_valid(mutex)) {
errno = EINVAL;
return(-1);
}
c->mutex = m; /* Remember we're using this mutex in a cond_wait */
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);
/* 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 */
if (mthread_mutex_lock(mutex) != 0)
return(-1);
return(0);
}

25
lib/libmthread/global.h Normal file
View file

@ -0,0 +1,25 @@
/* EXTERN should be extern, except for the allocate file */
#ifdef ALLOCATE
#undef EXTERN
#define EXTERN
#endif
#include <assert.h>
#define NO_THREADS 3
#define MAX_THREAD_POOL 1000
#define STACKSZ 4096
#define isokthreadid(i) (i >= 0 && i < no_threads)
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 fallback;
EXTERN mthread_tcb_t mainthread;
EXTERN int no_threads;
EXTERN int used_threads;
EXTERN int running_main_thread;

91
lib/libmthread/misc.c Normal file
View file

@ -0,0 +1,91 @@
#include <minix/mthread.h>
#include <stdio.h>
#include "proto.h"
#include "global.h"
/*===========================================================================*
* mthread_debug_f *
*===========================================================================*/
PUBLIC void mthread_debug_f(const char *file, int line, const char *msg)
{
/* Print debug message */
#ifdef MDEBUG
printf("MTH (%s:%d): %s\n", file, line, msg);
#endif
}
/*===========================================================================*
* mthread_panic_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(msg);
printf("\n");
exit(1);
}
/*===========================================================================*
* mthread_verify_f *
*===========================================================================*/
#ifdef MDEBUG
PUBLIC void mthread_verify_f(char *file, int line)
{
/* Verify library state. It is assumed this function is never called from
* a spawned thread, but from the 'main' thread. The library should be
* quiescent; no mutexes, conditions, or threads in use. All threads are to
* be in DEAD state.
*/
int i;
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;
conditions_ok = mthread_cond_verify();
mutexes_ok = mthread_mutex_verify();
attributes_ok = mthread_attr_verify();
printf("(%s:%d) VERIFY ", file, line);
printf("| threads: %s |", (threads_ok ? "ok": "NOT ok"));
printf("| cond: %s |", (conditions_ok ? "ok": "NOT ok"));
printf("| mutex: %s |", (mutexes_ok ? "ok": "NOT ok"));
printf("| attr: %s |", (attributes_ok ? "ok": "NOT ok"));
printf("\n");
if(!threads_ok || !conditions_ok || !mutexes_ok)
mthread_panic("Library state corrupt\n");
}
#else
PUBLIC void mthread_verify_f(char *f, int l) { ; }
#endif
/*===========================================================================*
* mthread_stats *
*===========================================================================*/
PUBLIC void mthread_stats(void)
{
int i, 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;
default: mthread_panic("Unknown state");
}
}
printf("Pool: %-5d In use: %-5d R: %-5d D: %-5d M: %-5d C: %-5d E: %-5d"
"F: %-5d\n",
no_threads, used_threads, st_run, st_dead, st_mutex, st_cond,
st_exit, st_fbexit);
}

288
lib/libmthread/mutex.c Normal file
View file

@ -0,0 +1,288 @@
#include <minix/mthread.h>
#include "proto.h"
#include "global.h"
PRIVATE struct __mthread_mutex *vm_front, *vm_rear;
FORWARD _PROTOTYPE( void mthread_mutex_add, (mthread_mutex_t *m) );
FORWARD _PROTOTYPE( void mthread_mutex_remove, (mthread_mutex_t *m) );
/*===========================================================================*
* mthread_init_valid_mutexes *
*===========================================================================*/
PUBLIC void mthread_init_valid_mutexes(void)
{
/* Initialize list of valid mutexes */
vm_front = vm_rear = NULL;
}
/*===========================================================================*
* mthread_mutex_add *
*===========================================================================*/
PRIVATE void mthread_mutex_add(m)
mthread_mutex_t *m;
{
/* Add mutex to list of valid, initialized mutexes */
if (vm_front == NULL) { /* Empty list */
vm_front = *m;
(*m)->prev = NULL;
} else {
vm_rear->next = *m;
(*m)->prev = vm_rear;
}
(*m)->next = NULL;
vm_rear = *m;
}
/*===========================================================================*
* mthread_mutex_destroy *
*===========================================================================*/
PUBLIC int mthread_mutex_destroy(mutex)
mthread_mutex_t *mutex;
{
/* Invalidate mutex and deallocate resources. */
int i;
mthread_init(); /* Make sure mthreads is initialized */
if (mutex == NULL) {
errno = EINVAL;
return(-1);
}
if (!mthread_mutex_valid(mutex)) {
errno = EINVAL;
return(-1);
} else if ((*mutex)->owner != NO_THREAD) {
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) {
errno = EBUSY;
return(-1);
}
}
}
/* Not in use; invalidate it */
mthread_mutex_remove(mutex);
free(*mutex);
*mutex = NULL;
return(0);
}
/*===========================================================================*
* mthread_mutex_init *
*===========================================================================*/
PUBLIC int mthread_mutex_init(mutex, mattr)
mthread_mutex_t *mutex; /* Mutex that is to be initialized */
mthread_mutexattr_t *mattr; /* Mutex attribute */
{
/* Initialize the mutex to a known state. Attributes are not supported */
struct __mthread_mutex *m;
mthread_init(); /* Make sure mthreads is initialized */
if (mutex == NULL) {
errno = EAGAIN;
return(-1);
} else if (mattr != NULL) {
errno = ENOSYS;
return(-1);
} else if (mthread_mutex_valid(mutex)) {
errno = EBUSY;
return(-1);
}
if ((m = malloc(sizeof(struct __mthread_mutex))) == NULL)
return(-1);
mthread_queue_init( &(m->queue) );
m->owner = NO_THREAD;
*mutex = (mthread_mutex_t) m;
mthread_mutex_add(mutex); /* Validate mutex; mutex now in use */
return(0);
}
/*===========================================================================*
* mthread_mutex_lock *
*===========================================================================*/
PUBLIC int mthread_mutex_lock(mutex)
mthread_mutex_t *mutex; /* Mutex that is to be locked */
{
/* Try to lock this mutex. If already locked, append the current thread to
* FIFO queue associated with this mutex and suspend the thread. */
struct __mthread_mutex *m;
mthread_init(); /* Make sure mthreads is initialized */
if (mutex == NULL) {
errno = EINVAL;
return(-1);
}
m = (struct __mthread_mutex *) *mutex;
if (!mthread_mutex_valid(&m)) {
errno = EINVAL;
return(-1);
} else if (m->owner == NO_THREAD) { /* Not locked */
m->owner = current_thread;
} else if (m->owner == current_thread) {
errno = EDEADLK;
return(-1);
} else {
mthread_queue_add( &(m->queue), current_thread);
mthread_suspend(MUTEX);
}
/* When we get here we acquired the lock. */
return(0);
}
/*===========================================================================*
* mthread_mutex_remove *
*===========================================================================*/
PRIVATE void mthread_mutex_remove(m)
mthread_mutex_t *m;
{
/* Remove mutex from list of valid, initialized mutexes */
if ((*m)->prev == NULL)
vm_front = (*m)->next;
else
(*m)->prev->next = (*m)->next;
if ((*m)->next == NULL)
vm_rear = (*m)->prev;
else
(*m)->next->prev = (*m)->prev;
}
/*===========================================================================*
* mthread_mutex_trylock *
*===========================================================================*/
PUBLIC int mthread_mutex_trylock(mutex)
mthread_mutex_t *mutex; /* Mutex that is to be locked */
{
/* Try to lock this mutex and return OK. If already locked, return error. */
struct __mthread_mutex *m;
mthread_init(); /* Make sure mthreads is initialized */
if (mutex == NULL) {
errno = EINVAL;
return(-1);
}
m = (struct __mthread_mutex *) *mutex;
if (!mthread_mutex_valid(&m)) {
errno = EINVAL;
return(-1);
} else if (m->owner == NO_THREAD) {
m->owner = current_thread;
return(0);
}
errno = EBUSY;
return(-1);
}
/*===========================================================================*
* mthread_mutex_unlock *
*===========================================================================*/
PUBLIC int mthread_mutex_unlock(mutex)
mthread_mutex_t *mutex; /* Mutex that is to be unlocked */
{
/* Unlock a previously locked mutex. If there is a pending lock for this mutex
* by another thread, mark that thread runnable. */
struct __mthread_mutex *m;
mthread_init(); /* Make sure mthreads is initialized */
if (mutex == NULL) {
errno = EINVAL;
return(-1);
}
m = (struct __mthread_mutex *) *mutex;
if (!mthread_mutex_valid(&m)) {
errno = EINVAL;
return(-1);
} else if (m->owner != current_thread) {
errno = EPERM;
return(-1); /* Can't unlock a mutex locked by another thread. */
}
m->owner = mthread_queue_remove( &(m->queue) );
if (m->owner != NO_THREAD) mthread_unsuspend(m->owner);
return(0);
}
/*===========================================================================*
* mthread_mutex_valid *
*===========================================================================*/
PUBLIC int mthread_mutex_valid(m)
mthread_mutex_t *m;
{
/* Check to see if mutex is on the list of valid mutexes */
struct __mthread_mutex *loopitem;
mthread_init(); /* Make sure mthreads is initialized */
loopitem = vm_front;
while (loopitem != NULL) {
if (loopitem == *m)
return(1);
loopitem = loopitem->next;
}
return(0);
}
/*===========================================================================*
* mthread_mutex_verify *
*===========================================================================*/
#ifdef MDEBUG
PUBLIC int mthread_mutex_verify(void)
{
/* Return true when no mutexes are in use */
int r = 1;
struct __mthread_mutex *loopitem;
mthread_init(); /* Make sure mthreads is initialized */
loopitem = vm_front;
while (loopitem != NULL) {
loopitem = loopitem->next;
r = 0;
}
return(r);
}
#endif

39
lib/libmthread/proto.h Normal file
View file

@ -0,0 +1,39 @@
#ifndef __MTHREAD_PROTO_H__
#define __MTHREAD_PROTO_H__
/* attribute.c */
_PROTOTYPE( void mthread_init_valid_attributes, (void) );
#ifdef MDEBUG
_PROTOTYPE( int mthread_attr_verify, (void) );
#endif
/* cond.c */
_PROTOTYPE( void mthread_init_valid_conditions, (void) );
#ifdef MDEBUG
_PROTOTYPE( int mthread_cond_verify, (void) );
#endif
/* misc.c */
#define mthread_panic(m) mthread_panic_f(__FILE__, __LINE__, (m))
_PROTOTYPE( void mthread_panic_f, (const char *file, int line,
const char *msg) );
#define mthread_debug(m) mthread_debug_f(__FILE__, __LINE__, (m))
_PROTOTYPE( void mthread_debug_f, (const char *file, int line,
const char *msg) );
/* mutex.c */
_PROTOTYPE( void mthread_init_valid_mutexes, (void) );
_PROTOTYPE( int mthread_mutex_valid, (mthread_mutex_t *mutex) );
#ifdef MDEBUG
_PROTOTYPE( int mthread_mutex_verify, (void) );
#endif
/* schedule.c */
_PROTOTYPE( int mthread_getcontext, (ucontext_t *ctxt) );
_PROTOTYPE( void mthread_init_scheduler, (void) );
/* queue.c */
_PROTOTYPE( void mthread_dump_queue, (mthread_queue_t *queue) );
#endif

105
lib/libmthread/queue.c Normal file
View file

@ -0,0 +1,105 @@
#include <minix/mthread.h>
#include "global.h"
/*===========================================================================*
* mthread_queue_add *
*===========================================================================*/
PUBLIC void mthread_queue_add(queue, thread)
mthread_queue_t *queue; /* Queue we want thread to append to */
mthread_thread_t thread;
{
/* Append a thread to the tail of the queue. As a process can be present on
* 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.
*/
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 */
}
}
/*===========================================================================*
* mthread_queue_init *
*===========================================================================*/
PUBLIC void mthread_queue_init(queue)
mthread_queue_t *queue; /* Queue that has to be initialized */
{
/* Initialize queue to a known state */
queue->head = queue->tail = NO_THREAD;
}
/*===========================================================================*
* mthread_queue_isempty *
*===========================================================================*/
PUBLIC int mthread_queue_isempty(queue)
mthread_queue_t *queue;
{
return(queue->head == NO_THREAD);
}
/*===========================================================================*
* mthread_dump_queue *
*===========================================================================*/
PUBLIC void mthread_dump_queue(queue)
mthread_queue_t *queue;
{
int threshold, count = 0;
mthread_thread_t t;
threshold = no_threads;
#ifdef MDEBUG
printf("Dumping queue: ");
#endif
if(queue->head != NO_THREAD) {
t = queue->head;
#ifdef MDEBUG
printf("%d ", t);
#endif
count++;
t = threads[t].m_next;
while (t != NO_THREAD) {
#ifdef MDEBUG
printf("%d ", t);
#endif
t = threads[t].m_next;
count++;
if (count > threshold) break;
}
} else {
#ifdef MDEBUG
printf("[empty]");
#endif
}
#ifdef MDEBUG
printf("\n");
#endif
}
/*===========================================================================*
* mthread_queue_remove *
*===========================================================================*/
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;
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
/* Second thread in line is the new first */
queue->head = threads[thread].m_next;
}
return(thread);
}

197
lib/libmthread/scheduler.c Normal file
View file

@ -0,0 +1,197 @@
#include <minix/mthread.h>
#include "global.h"
#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
PRIVATE int yield_all;
/*===========================================================================*
* mthread_getcontext *
*===========================================================================*/
PUBLIC int mthread_getcontext(ctx)
ucontext_t *ctx;
{
/* Retrieve this process' current state.*/
/* We're not interested in FPU state nor signals, so ignore them.
* Coincidentally, this significantly speeds up performance.
*/
ctx->uc_flags |= (UCF_IGNFPU | UCF_IGNSIGM);
return getcontext(ctx);
}
/*===========================================================================*
* mthread_schedule *
*===========================================================================*/
PUBLIC void mthread_schedule(void)
{
/* Pick a new thread to run and run it. In practice, this involves taking the
* first thread off the (FIFO) run queue and resuming that thread.
*/
int old_thread;
ucontext_t *new_ctx, *old_ctx;
mthread_init(); /* Make sure mthreads is initialized */
old_thread = current_thread;
if (mthread_queue_isempty(&run_queue)) {
/* No runnable threads. Let main thread run. */
/* We keep track whether we're running the program's 'main' thread or
* a spawned thread. In case we're already running the main thread and
* there are no runnable threads, we can't jump back to its context.
* Instead, we simply return.
*/
if (running_main_thread) return;
/* We're running the last runnable spawned thread. Return to main
* thread as there is no work left.
*/
running_main_thread = 1;
current_thread = NO_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;
if (old_thread == NO_THREAD)
old_ctx = MAIN_CTX;
else
old_ctx = OLD_CTX;
if (swapcontext(old_ctx, new_ctx) == -1)
mthread_panic("Could not swap context");
}
/*===========================================================================*
* mthread_init_scheduler *
*===========================================================================*/
PUBLIC void mthread_init_scheduler(void)
{
/* Initialize the scheduler */
mthread_queue_init(&run_queue);
yield_all = 0;
}
/*===========================================================================*
* mthread_suspend *
*===========================================================================*/
PUBLIC void mthread_suspend(state)
mthread_state_t state;
{
/* Stop the current thread from running. There can be multiple reasons for
* this; the process tries to lock a locked mutex (i.e., has to wait for it to
* become unlocked), the process has to wait for a condition, the thread
* volunteered to let another thread to run (i.e., it called yield and remains
* runnable itself), or the thread is dead.
*/
int continue_thread = 0;
if (state == DEAD) mthread_panic("Shouldn't suspend with DEAD state");
threads[current_thread].m_state = state;
/* Save current thread's context */
if (mthread_getcontext(CURRENT_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
* or swapcontext.
*/
if(!continue_thread) {
continue_thread = 1;
mthread_schedule(); /* Let other thread run. */
}
}
/*===========================================================================*
* mthread_unsuspend *
*===========================================================================*/
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 */
if (!isokthreadid(thread)) mthread_panic("Invalid thread id\n");
threads[thread].m_state = RUNNABLE;
mthread_queue_add(&run_queue, thread);
}
/*===========================================================================*
* mthread_yield *
*===========================================================================*/
PUBLIC int mthread_yield(void)
{
/* Defer further execution of the current thread and let another thread run. */
mthread_init(); /* Make sure mthreads is initialized */
if (mthread_queue_isempty(&run_queue)) { /* No point in yielding. */
return(-1);
} else if (current_thread == NO_THREAD) {
/* Can't yield this thread, but still give other threads a chance to
* run.
*/
mthread_schedule();
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.
*/
return(0);
}
/*===========================================================================*
* mthread_yield_all *
*===========================================================================*/
PUBLIC void mthread_yield_all(void)
{
/* Yield until there are no more runnable threads left. Two threads calling
* this function will lead to a deadlock.
*/
mthread_init(); /* Make sure mthreads is initialized */
if (yield_all) mthread_panic("Deadlock: two threads trying to yield_all");
yield_all = 1;
/* This works as follows. Thread A is running and threads B, C, and D are
* runnable. As A is running, it is NOT on the run_queue (see
* mthread_schedule). It calls mthread_yield and will be added to the run
* queue, allowing B to run. B runs and suspends eventually, possibly still
* in a runnable state. Then C and D run. Eventually A will run again (and is
* thus not on the list). If B, C, and D are dead, waiting for a condition,
* or waiting for a lock, they are not on the run queue either. At that
* point A is the only runnable thread left.
*/
while (!mthread_queue_isempty(&run_queue)) {
(void) mthread_yield();
}
/* Done yielding all threads. */
yield_all = 0;
}

View file

@ -19,7 +19,7 @@ ROOTOBJ= test11 test33 test43 test44 test46
GCCOBJ= test45-gcc test49-gcc
GCCFPUOBJ= test51-gcc test52-gcc
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(GCCFPUOBJ) $(ROOTOBJ) test57
all: $(OBJ) $(BIGOBJ) $(GCCOBJ) $(GCCFPUOBJ) $(ROOTOBJ) test57 test59
chmod 755 *.sh run
$(OBJ):
@ -115,3 +115,5 @@ test56: test56.c
test57: test57.c test57loop.S
if which $(GCC) >/dev/null 2>&1; then $(GCC) $(CFLAGS-GCC) -o $@ test57.c test57loop.S; fi
test58: test58.c
test59: test59.c
$(CC) $(CFLAGS) -o $@ $@.c -lmthread

View file

@ -14,7 +14,7 @@ badones= # list of tests that failed
tests=" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 \
41 42 43 44 45 45-gcc 46 47 48 49 49-gcc 50 \
51 51-gcc 52 52-gcc 53 54 55 56 57 58\
51 51-gcc 52 52-gcc 53 54 55 56 57 58 59\
sh1.sh sh2.sh"
tests_no=`expr 0`

682
test/test59.c Normal file
View file

@ -0,0 +1,682 @@
/* Test the mthreads library. When the library is compiled with -DMDEBUG, you
* have to compile this test with -DMDEBUG as well or it won't link. MDEBUG
* lets you check the internal integrity of the library. */
#include <stdio.h>
#include <minix/mthread.h>
#define thread_t mthread_thread_t
#define mutex_t mthread_mutex_t
#define cond_t mthread_cond_t
#define once_t mthread_once_t
#define attr_t mthread_attr_t
#define MAX_ERROR 5
#include "common.c"
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 cond_t condition;
PRIVATE mutex_t *count_mutex, *condition_mutex;
PRIVATE once_t once;
#define ROUNDS 14
#define THRESH1 3
#define THRESH2 8
#define MEG 1024*1024
#define MAGIC 0xb4a3f1c2
FORWARD _PROTOTYPE( void thread_a, (void *arg) );
FORWARD _PROTOTYPE( void thread_b, (void *arg) );
FORWARD _PROTOTYPE( void thread_c, (void *arg) );
FORWARD _PROTOTYPE( void thread_d, (void *arg) );
FORWARD _PROTOTYPE( void thread_e, (void) );
FORWARD _PROTOTYPE( void thread_f, (void *arg) );
FORWARD _PROTOTYPE( void thread_g, (void *arg) );
FORWARD _PROTOTYPE( void thread_h, (void *arg) );
FORWARD _PROTOTYPE( void test_scheduling, (void) );
FORWARD _PROTOTYPE( void test_mutex, (void) );
FORWARD _PROTOTYPE( void test_condition, (void) );
FORWARD _PROTOTYPE( void test_attributes, (void) );
FORWARD _PROTOTYPE( void err, (int subtest, int error) );
/*===========================================================================*
* thread_a *
*===========================================================================*/
PRIVATE void thread_a(void *arg) {
th_a++;
}
/*===========================================================================*
* thread_b *
*===========================================================================*/
PRIVATE void thread_b(void *arg) {
th_b++;
if (mthread_once(&once, thread_e) != 0) err(10, 1);
}
/*===========================================================================*
* thread_c *
*===========================================================================*/
PRIVATE void thread_c(void *arg) {
th_c++;
}
/*===========================================================================*
* thread_d *
*===========================================================================*/
PRIVATE void thread_d(void *arg) {
th_d++;
mthread_exit(NULL); /* Thread wants to stop running */
}
/*===========================================================================*
* thread_e *
*===========================================================================*/
PRIVATE void thread_e(void) {
th_e++;
}
/*===========================================================================*
* thread_f *
*===========================================================================*/
PRIVATE void thread_f(void *arg) {
if (mthread_mutex_lock(condition_mutex) != 0) err(12, 1);
th_f++;
if (mthread_cond_signal(&condition) != 0) err(12, 2);
if (mthread_mutex_unlock(condition_mutex) != 0) err(12, 3);
}
/*===========================================================================*
* thread_g *
*===========================================================================*/
PRIVATE void thread_g(void *arg) {
char bigarray[MTHREAD_STACK_MIN + 1];
if (mthread_mutex_lock(condition_mutex) != 0) err(13, 1);
memset(bigarray, '\0', MTHREAD_STACK_MIN + 1); /* Actually allocate it */
th_g++;
if (mthread_cond_signal(&condition) != 0) err(13, 2);
if (mthread_mutex_unlock(condition_mutex) != 0) err(13, 3);
}
/*===========================================================================*
* thread_h *
*===========================================================================*/
PRIVATE void thread_h(void *arg) {
char bigarray[2 * MEG];
int reply;
if (mthread_mutex_lock(condition_mutex) != 0) err(14, 1);
memset(bigarray, '\0', 2 * MEG); /* Actually allocate it */
th_h++;
if (mthread_cond_signal(&condition) != 0) err(14, 2);
if (mthread_mutex_unlock(condition_mutex) != 0) err(14, 3);
reply = *((int *) arg);
mthread_exit((void *) reply);
}
/*===========================================================================*
* err *
*===========================================================================*/
PRIVATE void err(int sub, int error) {
/* As we're running with multiple threads, they might all clobber the
* subtest variable. This wrapper prevents that from happening. */
subtest = sub;
e(error);
}
/*===========================================================================*
* test_scheduling *
*===========================================================================*/
PRIVATE void test_scheduling(void)
{
int i;
thread_t t[7];
#ifdef MDEBUG
mthread_verify();
#endif
th_a = th_b = th_c = th_d = th_e = 0;
if (mthread_create(&t[0], NULL, thread_a, NULL) != 0) err(1, 1);
if (mthread_create(&t[1], NULL, thread_a, NULL) != 0) err(1, 2);
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();
if (mthread_create(&t[4], NULL, thread_c, NULL) != 0) err(1, 6);
mthread_schedule();
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();
if (mthread_once(&once, thread_e) != 0) err(1, 9);
if (mthread_once(&once, thread_e) != 0) err(1, 10);
if (th_a != 4) err(1, 11);
if (th_b != 1) err(1, 12);
if (th_c != 1) err(1, 13);
if (th_d != 1) err(1, 14);
if (th_e != 1) err(1, 15);
for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++) {
if (mthread_join(t[i], NULL) != 0) err(1, 16);
if (mthread_join(t[i], NULL) == 0) err(1, 17); /*Shouldn't work twice*/
}
#ifdef MDEBUG
mthread_verify();
#endif
if (mthread_create(NULL, NULL, NULL, NULL) == 0) err(1, 18);
mthread_yield();
#ifdef MDEBUG
mthread_verify();
#endif
if (mthread_create(&t[6], NULL, NULL, NULL) == 0) err(1, 19);
mthread_yield();
#ifdef MDEBUG
mthread_verify();
#endif
if (mthread_join(0xc0ffee, NULL) == 0) err(1, 20);
mthread_yield();
mthread_yield();
#ifdef MDEBUG
mthread_verify();
#endif
}
/*===========================================================================*
* mutex_a *
*===========================================================================*/
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);
/* Trying to acquire lock again should fail with EDEADLK */
if (mthread_mutex_lock(&mu[0]) != -1) err(3, 2);
if (errno != EDEADLK) err(3, 3);
/* Try to acquire lock on uninitialized mutex; should fail with EINVAL */
if (mthread_mutex_lock(&mu2) != -1) {
err(3, 4);
mthread_mutex_unlock(&mu2);
}
if (errno != EINVAL) err(3, 5);
errno = 0;
if (mthread_mutex_trylock(&mu2) != -1) {
err(3, 6);
mthread_mutex_unlock(&mu2);
}
if (errno != EINVAL) err(3, 7);
if (mthread_mutex_trylock(&mu[1]) != 0) err(3, 8);
mthread_yield();
if (mthread_mutex_unlock(&mu[0]) != 0) err(3, 9);
mthread_yield();
if (mthread_mutex_unlock(&mu[1]) != 0) err(3, 10);
/* 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);
}
/*===========================================================================*
* mutex_b *
*===========================================================================*/
PRIVATE void mutex_b(void *arg)
{
mutex_t *mu = (mutex_t *) arg;
/* At this point mutex_a thread should have acquired a lock on mu[0]. We
* 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);
/* 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_lock(&mu[0]) != 0) err(4, 5);
if (mthread_mutex_lock(&mu[1]) != 0) err(4, 6);
mthread_yield();
if (mthread_mutex_unlock(&mu[0]) != 0) err(4, 7);
mthread_yield();
if (mthread_mutex_unlock(&mu[1]) != 0) err(4, 8);
}
/*===========================================================================*
* mutex_c *
*===========================================================================*/
PRIVATE void mutex_c(void *arg)
{
mutex_t *mu = (mutex_t *) arg;
if (mthread_mutex_lock(&mu[1]) != 0) err(5, 1);
mthread_yield();
if (mthread_mutex_unlock(&mu[1]) != 0) err(5, 2);
if (mthread_mutex_lock(&mu[0]) != 0) err(5, 3);
mthread_yield();
if (mthread_mutex_unlock(&mu[0]) != 0) err(5, 4);
}
/*===========================================================================*
* test_mutex *
*===========================================================================*/
PRIVATE void test_mutex(void)
{
int i;
thread_t t[3];
#ifdef MDEBUG
mthread_verify();
#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_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);
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
* to acquire locks on the mutexes and exit.
*/
for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++)
if (mthread_join(t[i], NULL) != 0) err(2, 6);
if (mthread_mutex_destroy(&mu[0]) != 0) err(2, 7);
if (mthread_mutex_destroy(&mu[1]) != 0) err(2, 8);
#ifdef MDEBUG
mthread_verify();
#endif
}
/*===========================================================================*
* cond_a *
*===========================================================================*/
PRIVATE void cond_a(void *arg)
{
cond_t c;
int did_count = 0;
while(1) {
if (mthread_mutex_lock(condition_mutex) != 0) err(6, 1);
while (count >= THRESH1 && count <= THRESH2) {
if (mthread_cond_wait(&condition, condition_mutex) != 0)
err(6, 2);
}
if (mthread_mutex_unlock(condition_mutex) != 0) err(6, 3);
mthread_yield();
if (mthread_mutex_lock(count_mutex) != 0) err(6, 4);
count++;
did_count++;
if (mthread_mutex_unlock(count_mutex) != 0) err(6, 5);
if (count >= ROUNDS) break;
}
if (!(did_count <= count - (THRESH2 - THRESH1 + 1))) err(6, 6);
/* Try faulty addresses */
if (mthread_mutex_lock(condition_mutex) != 0) err(6, 7);
/* Condition c is not initialized, so whatever we do with it should fail. */
if (mthread_cond_wait(&c, condition_mutex) == 0) err(6, 8);
if (mthread_cond_wait(NULL, condition_mutex) == 0) err(6, 9);
if (mthread_cond_signal(&c) == 0) err(6, 10);
if (mthread_mutex_unlock(condition_mutex) != 0) err(6, 11);
/* Try again with an unlocked mutex */
if (mthread_cond_wait(&c, condition_mutex) == 0) err(6, 12);
if (mthread_cond_signal(&c) == 0) err(6, 13);
/* And again with an unlocked mutex, but initialized c */
if (mthread_cond_init(&c, NULL) != 0) err(6, 14);
if (mthread_cond_wait(&c, condition_mutex) == 0) err(6, 15);
if (mthread_cond_signal(&c) != 0) err(6, 16);/*c.f., 6.10 this should work!*/
if (mthread_cond_destroy(&c) != 0) err(6, 17);
}
/*===========================================================================*
* cond_b *
*===========================================================================*/
PRIVATE void cond_b(void *arg)
{
int did_count = 0;
while(1) {
if (mthread_mutex_lock(condition_mutex) != 0) err(7, 1);
if (count < THRESH1 || count > THRESH2)
if (mthread_cond_signal(&condition) != 0) err(7, 2);
if (mthread_mutex_unlock(condition_mutex) != 0) err(7, 3);
mthread_yield();
if (mthread_mutex_lock(count_mutex) != 0) err(7, 4);
count++;
did_count++;
if (mthread_mutex_unlock(count_mutex) != 0) err(7, 5);
if (count >= ROUNDS) break;
}
if (!(did_count >= count - (THRESH2 - THRESH1 + 1))) err(7, 6);
}
/*===========================================================================*
* cond_broadcast *
*===========================================================================*/
PRIVATE void cond_broadcast(void *arg)
{
int rounds = 0;
if (mthread_mutex_lock(condition_mutex) != 0) err(9, 1);
while(!condition_met)
if (mthread_cond_wait(&condition, condition_mutex) != 0) err(9, 2);
if (mthread_mutex_unlock(condition_mutex) != 0) err(9, 3);
if (mthread_mutex_lock(count_mutex) != 0) err(9, 4);
count++;
if (mthread_mutex_unlock(count_mutex) != 0) err(9, 5);
}
/*===========================================================================*
* test_condition *
*===========================================================================*/
PRIVATE void test_condition(void)
{
#define NTHREADS 10
int i, r;
thread_t t[2], s[NTHREADS];
count_mutex = &mu[0];
condition_mutex = &mu[1];
/* Test simple condition variable behavior: Two threads increase a counter.
* At some point one thread waits for a condition and the other thread
* signals the condition. Consequently, one thread increased the counter a
* few times less than other thread. Although the difference is 'random',
* there is a guaranteed minimum difference that we can measure.
*/
#ifdef MDEBUG
mthread_verify();
#endif
if (mthread_mutex_init(count_mutex, NULL) != 0) err(8, 1);
if (mthread_mutex_init(condition_mutex, NULL) != 0) err(8, 2);
if (mthread_cond_init(&condition, NULL) != 0) err(8, 3);
count = 0;
if (mthread_create(&t[0], NULL, cond_a, NULL) != 0) err(8, 4);
if (mthread_create(&t[1], NULL, cond_b, NULL) != 0) err(8, 5);
for (i = 0; i < (sizeof(t) / sizeof(thread_t)); i++)
if (mthread_join(t[i], NULL) != 0) err(8, 6);
if (mthread_mutex_destroy(count_mutex) != 0) err(8, 7);
if (mthread_mutex_destroy(condition_mutex) != 0) err(8, 8);
if (mthread_cond_destroy(&condition) != 0) err(8, 9);
/* Let's try to destroy it again. Should fails as it's uninitialized. */
if (mthread_cond_destroy(&condition) == 0) err(8, 10);
#ifdef MDEBUG
mthread_verify();
#endif
/* Test signal broadcasting: spawn N threads that will increase a counter
* after a condition has been signaled. The counter must equal N. */
if (mthread_mutex_init(count_mutex, NULL) != 0) err(8, 11);
if (mthread_mutex_init(condition_mutex, NULL) != 0) err(8, 12);
if (mthread_cond_init(&condition, NULL) != 0) err(8, 13);
condition_met = count = 0;
for (i = 0; i < NTHREADS; i++)
if (mthread_create(&s[i], NULL, cond_broadcast, NULL) != 0) err(8, 14);
/* Allow other threads to block on the condition variable. If we don't yield,
* the threads will only start running when we call mthread_join below. In
* that case the while loop in cond_broadcast will never evaluate to true.
*/
mthread_yield();
if (mthread_mutex_lock(condition_mutex) != 0) err(8, 15);
condition_met = 1;
if (mthread_cond_broadcast(&condition) != 0) err(8, 16);
if (mthread_mutex_unlock(condition_mutex) != 0) err(8, 17);
for (i = 0; i < (sizeof(s) / sizeof(thread_t)); i++)
if (mthread_join(s[i], NULL) != 0) err(8, 18);
if (count != NTHREADS) err(8, 19);
if (mthread_mutex_destroy(count_mutex) != 0) err(8, 20);
if (mthread_mutex_destroy(condition_mutex) != 0) err(8, 21);
if (mthread_cond_destroy(&condition) != 0) err(8, 22);
/* Again, destroying the condition variable twice shouldn't work */
if (mthread_cond_destroy(&condition) == 0) err(8, 23);
#ifdef MDEBUG
mthread_verify();
#endif
}
/*===========================================================================*
* test_attributes *
*===========================================================================*/
PRIVATE void test_attributes(void)
{
attr_t tattr;
thread_t tid;
int detachstate = -1, status = 0;
int i, no_ints, stack_untouched = 1;
void *stackaddr, *newstackaddr;
int *stackp;
size_t stacksize, newstacksize;
#ifdef MDEBUG
mthread_verify();
#endif
/* Initialize thread attribute and try to read the default values */
if (mthread_attr_init(&tattr) != 0) err(11, 1);
if (mthread_attr_getdetachstate(&tattr, &detachstate) != 0) err(11, 2);
if (detachstate != MTHREAD_CREATE_JOINABLE) err(11, 3);
if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 4);
if (stackaddr != NULL) err(11, 5);
if (stacksize != (size_t) 0) err(11, 6);
/* Modify the attribute ... */
/* Try bogus detach state value */
if (mthread_attr_setdetachstate(&tattr, 0xc0ffee) == 0) err(11, 7);
if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
err(11, 8);
newstacksize = (size_t) MEG;
if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 9);
if (mthread_attr_setstack(&tattr, newstackaddr, newstacksize) != 0)
err(11, 10);
/* ... and read back the new values. */
if (mthread_attr_getdetachstate(&tattr, &detachstate) != 0) err(11, 11);
if (detachstate != MTHREAD_CREATE_DETACHED) err(11, 12);
if (mthread_attr_getstack(&tattr, &stackaddr, &stacksize) != 0) err(11, 13);
if (stackaddr != newstackaddr) err(11, 14);
if (stacksize != newstacksize) err(11, 15);
/* 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. */
free(newstackaddr);
/* Try to allocate too small a stack; it should fail and the attribute
* values should remain as is.
*/
newstacksize = MTHREAD_STACK_MIN - 1;
if ((newstackaddr = malloc(newstacksize)) == NULL) err(11, 16);
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);
/* Again, freeing because we can. Shouldn't do it if it was actually used. */
free(newstackaddr);
/* Tell attribute to let the system allocate a stack for the thread and only
* dictate how big that stack should be (2 megabyte, not actually allocated
* yet).
*/
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);
/* 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);
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);
/* Perform same tests, but also actually use them in a thread */
if (mthread_attr_init(&tattr) != 0) err(11, 31);
if (mthread_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
err(11, 32);
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);
/* 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);
/* Joining a detached thread should fail */
if (mthread_join(tid, NULL) == 0) err(11, 40);
if (mthread_attr_destroy(&tattr) != 0) err(11, 41);
/* Try telling the attribute how large the stack should be */
if (mthread_attr_init(&tattr) != 0) err(11, 42);
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);
/* 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_attr_setdetachstate(&tattr, MTHREAD_CREATE_DETACHED) != 0)
err(11, 49); /* 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);
/* Try telling the attribute how large the stack should be and where it is
* located.
*/
if (mthread_attr_init(&tattr) != 0) err(11, 52);
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);
/* 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
* zeroes, so we can check if that's true.
*/
stackp = stackaddr;
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_create(&tid, &tattr, thread_h, (void *) &stacksize) != 0)
err(11, 57);
/* Wait for thread h to finish */
if (mthread_cond_wait(&condition, condition_mutex) != 0) err(11, 58);
if (th_h != 1) err(11, 59);
/* 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 */
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*/
#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);
#ifdef MDEBUG
mthread_verify();
#endif
}
/*===========================================================================*
* main *
*===========================================================================*/
int main(void)
{
errct = 0;
th_a = th_b = th_c = th_d = th_e = th_f = th_g = th_h = 0;
once = MTHREAD_ONCE_INIT;
start(59);
mthread_init();
test_scheduling();
test_mutex();
test_condition();
test_attributes();
quit();
}