minix/lib/libmthread/mutex.c
Thomas Veerman c8d0edc06a - Refactor mthread structure fields to prevent name clashes
- Remove sanity checks for initialized mutexes and condition variables. This
  significantly boosts performance. The checks can be turned back on by
  compiling libmthread with MTHREAD_STRICT. According to POSIX operations on
  uninitialized variables are a MAY fail if, therefore allowing this
  optimization.
- Test59 has to be accommodated to the lack of sanity checks on uninitialized
  variables in the library. It specifically tests for them and will run into
  segfaults when the checks are absent in the library.
- Fix a few bugs related to the scheduler
- Do some general code cleanups
2011-03-18 10:29:54 +00:00

307 lines
7.8 KiB
C

#include <minix/mthread.h>
#include "global.h"
#include "proto.h"
PRIVATE struct __mthread_mutex *vm_front, *vm_rear;
#ifdef MTHREAD_STRICT
FORWARD _PROTOTYPE( void mthread_mutex_add, (mthread_mutex_t *m) );
FORWARD _PROTOTYPE( void mthread_mutex_remove, (mthread_mutex_t *m) );
#else
# define mthread_mutex_add(m) ((*m)->mm_magic = MTHREAD_INIT_MAGIC)
# define mthread_mutex_remove(m) ((*m)->mm_magic = MTHREAD_NOT_INUSE)
#endif
/*===========================================================================*
* mthread_init_valid_mutexes *
*===========================================================================*/
PUBLIC void mthread_init_valid_mutexes(void)
{
/* Initialize list of valid mutexes */
vm_front = vm_rear = NULL;
}
/*===========================================================================*
* mthread_mutex_add *
*===========================================================================*/
#ifdef MTHREAD_STRICT
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)->mm_prev = NULL;
} else {
vm_rear->mm_next = *m;
(*m)->mm_prev = vm_rear;
}
(*m)->mm_next = NULL;
vm_rear = *m;
}
#endif
/*===========================================================================*
* 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)->mm_owner != NO_THREAD) {
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->mc_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);
}
#ifdef MTHREAD_STRICT
else if (mthread_mutex_valid(mutex)) {
errno = EBUSY;
return(-1);
}
#endif
else if ((m = malloc(sizeof(struct __mthread_mutex))) == NULL) {
errno = ENOMEM;
return(-1);
}
mthread_queue_init(&m->mm_queue);
m->mm_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->mm_owner == NO_THREAD) { /* Not locked */
m->mm_owner = current_thread;
if (current_thread == MAIN_THREAD)
mthread_debug("MAIN_THREAD now mutex owner\n");
} else if (m->mm_owner == current_thread) {
errno = EDEADLK;
return(-1);
} else {
mthread_queue_add(&m->mm_queue, current_thread);
if (m->mm_owner == MAIN_THREAD)
mthread_dump_queue(&m->mm_queue);
mthread_suspend(MS_MUTEX);
}
/* When we get here we acquired the lock. */
return(0);
}
/*===========================================================================*
* mthread_mutex_remove *
*===========================================================================*/
#ifdef MTHREAD_STRICT
PRIVATE void mthread_mutex_remove(m)
mthread_mutex_t *m;
{
/* Remove mutex from list of valid, initialized mutexes */
if ((*m)->mm_prev == NULL)
vm_front = (*m)->mm_next;
else
(*m)->mm_prev->mm_next = (*m)->mm_next;
if ((*m)->mm_next == NULL)
vm_rear = (*m)->mm_prev;
else
(*m)->mm_next->mm_prev = (*m)->mm_prev;
}
#endif
/*===========================================================================*
* 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->mm_owner == NO_THREAD) {
m->mm_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->mm_owner != current_thread) {
errno = EPERM;
return(-1); /* Can't unlock a mutex locked by another thread. */
}
m->mm_owner = mthread_queue_remove(&m->mm_queue);
if (m->mm_owner != NO_THREAD) mthread_unsuspend(m->mm_owner);
return(0);
}
/*===========================================================================*
* mthread_mutex_valid *
*===========================================================================*/
#ifdef MTHREAD_STRICT
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->mm_next;
}
return(0);
}
#endif
/*===========================================================================*
* 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->mm_owner);
loopitem = loopitem->next;
r = 0;
}
return(r);
}
#endif