Home | History | Annotate | Line # | Download | only in libpthread
pthread_mutex.c revision 1.1.2.2
      1 /* Copyright */
      2 
      3 #include <assert.h>
      4 #include <errno.h>
      5 #include <signal.h>
      6 #include <stdlib.h>
      7 #include <ucontext.h>
      8 #include <sys/queue.h>
      9 
     10 #include "pthread.h"
     11 #include "pthread_int.h"
     12 
     13 
     14 int
     15 pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
     16 {
     17 
     18 	assert(mutex != NULL);
     19 
     20 	/* XXX No mutex attr support yet. */
     21 	if (attr != NULL)
     22 		return EINVAL;
     23 
     24 	/* Allocate. */
     25 
     26 	mutex->ptm_magic = PT_MUTEX_MAGIC;
     27 	mutex->ptm_owner = NULL;
     28 	pthread_lockinit(&mutex->ptm_lock);
     29 	pthread_lockinit(&mutex->ptm_interlock);
     30 	PTQ_INIT(&mutex->ptm_blocked);
     31 
     32 	return 0;
     33 }
     34 
     35 
     36 int
     37 pthread_mutex_destroy(pthread_mutex_t *mutex)
     38 {
     39 
     40 	assert(mutex != NULL);
     41 	assert(mutex->ptm_lock == __SIMPLELOCK_UNLOCKED);
     42 
     43 	mutex->ptm_magic = PT_MUTEX_DEAD;
     44 
     45 	return 0;
     46 }
     47 
     48 
     49 /*
     50  * Note regarding memory visibility: Pthreads has rules about memory
     51  * visibility and mutexes. Very roughly: Memory a thread can see when
     52  * it unlocks a mutex can be seen by another thread that locks the
     53  * same mutex.
     54  *
     55  * A memory barrier after a lock and before an unlock will provide
     56  * this behavior. This code relies on __cpu_simple_lock_try() to issue
     57  * a barrier after obtaining a lock, and on __cpu_simple_unlock() to
     58  * issue a barrier before releasing a lock.
     59  */
     60 
     61 int
     62 pthread_mutex_lock(pthread_mutex_t *mutex)
     63 {
     64 	pthread_t self;
     65 #ifdef ERRORCHECK
     66 	if ((mutex == NULL) || (mutex->ptm_magic != PT_MUTEX_MAGIC))
     67 		return EINVAL;
     68 #endif
     69 
     70 	while (/*CONSTCOND*/1) {
     71 		if (__cpu_simple_lock_try(&mutex->ptm_lock))
     72 		    break; /* got it! */
     73 
     74 		self = pthread__self();
     75 		/* Okay, didn't look free. Get the interlock... */
     76 		pthread_spinlock(self, &mutex->ptm_interlock);
     77 		/* The mutex_unlock routine will get the interlock
     78 		 * before looking at the list of sleepers, so if the
     79 		 * lock is held we can safely put ourselves on the
     80 		 * sleep queue. If it's not held, we can try taking it
     81 		 * again.
     82 		 */
     83 		if (mutex->ptm_lock == __SIMPLELOCK_LOCKED) {
     84 			PTQ_INSERT_TAIL(&mutex->ptm_blocked, self, pt_sleep);
     85 			self->pt_state = PT_STATE_BLOCKED;
     86 			pthread__block(self, &mutex->ptm_interlock);
     87 			/* interlock is not held when we return */
     88 		} else {
     89 			pthread_spinunlock(self, &mutex->ptm_interlock);
     90 		}
     91 		/* Go around for another try. */
     92 	}
     93 
     94 	/* We have the lock! */
     95 #ifdef ERRORCHECK
     96 	mutex->ptm_owner = self;
     97 #endif
     98 	return 0;
     99 }
    100 
    101 
    102 int
    103 pthread_mutex_trylock(pthread_mutex_t *mutex)
    104 {
    105 
    106 #ifdef ERRORCHECK
    107 	if ((mutex == NULL) || (mutex->ptm_magic != PT_MUTEX_MAGIC))
    108 		return EINVAL;
    109 #endif
    110 
    111 	if (__cpu_simple_lock_try(&mutex->ptm_lock) == 0)
    112 		return EBUSY;
    113 
    114 #ifdef ERRORCHECK
    115 	mutex->ptm_owner = pthread__self();
    116 #endif
    117 	return 0;
    118 }
    119 
    120 
    121 int
    122 pthread_mutex_unlock(pthread_mutex_t *mutex)
    123 {
    124 	pthread_t self, blocked;
    125 	struct pt_queue_t blockedq, nullq = PTQ_HEAD_INITIALIZER;
    126 
    127 #ifdef ERRORCHECK
    128 	if ((mutex == NULL) || (mutex->ptm_magic != PT_MUTEX_MAGIC))
    129 		return EINVAL;
    130 
    131 	if (mutex->ptm_lock != __SIMPLELOCK_LOCKED)
    132 		return EPERM; /* Not exactly the right error. */
    133 
    134 	/* One is only permitted to unlock one's own mutexes. */
    135 	if (mutex->ptm_owner != self)
    136 		return EPERM;
    137 #endif
    138 
    139 	self = pthread__self();
    140 	pthread_spinlock(self, &mutex->ptm_interlock);
    141        	blockedq = mutex->ptm_blocked;
    142 	mutex->ptm_blocked = nullq;
    143 #ifdef ERRORCHECK
    144 	mutex->ptm_owner = NULL;
    145 #endif
    146 	__cpu_simple_unlock(&mutex->ptm_lock);
    147 	pthread_spinunlock(self, &mutex->ptm_interlock);
    148 
    149 	/* Give everyone on the sleep queue another chance at the lock. */
    150 	PTQ_FOREACH(blocked, &blockedq, pt_sleep)
    151 		pthread__sched(self, blocked);
    152 
    153 	return 0;
    154 }
    155