minix/lib/libmthread/mutex.c
Thomas Veerman a7072a5e1c Revamp the mthread library and update test59
Before, the 'main thread' of a process was never taken into account anywhere in
the library, causing mutexes not to work properly (and consequently, neither
did the condition variables). For example, if the 'main thread' (that is, the
thread which is started at the beginning of a process; not a spawned thread by
the library) would lock a mutex, it wasn't actually locked.
2010-09-30 13:44:13 +00:00

298 lines
7.5 KiB
C

#include <minix/mthread.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) );
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. */
mthread_thread_t t;
mthread_tcb_t *tcb;
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) {
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 (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);
}
}
}
/* 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_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. */
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;
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);
if (m->owner == MAIN_THREAD)
mthread_dump_queue(&(m->queue));
mthread_suspend(MS_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) {
printf("mutex corruption: owner: %d\n", loopitem->owner);
loopitem = loopitem->next;
r = 0;
}
return(r);
}
#endif