Home | History | Annotate | Line # | Download | only in libpthread
pthread_mutex.c revision 1.1.2.17
      1 /*	$NetBSD: pthread_mutex.c,v 1.1.2.17 2003/01/08 19:34:22 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Nathan J. Williams.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. All advertising materials mentioning features or use of this software
     19  *    must display the following acknowledgement:
     20  *        This product includes software developed by the NetBSD
     21  *        Foundation, Inc. and its contributors.
     22  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23  *    contributors may be used to endorse or promote products derived
     24  *    from this software without specific prior written permission.
     25  *
     26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36  * POSSIBILITY OF SUCH DAMAGE.
     37  */
     38 
     39 #include <assert.h>
     40 #include <errno.h>
     41 #include <sys/cdefs.h>
     42 
     43 #include "pthread.h"
     44 #include "pthread_int.h"
     45 
     46 static void pthread_mutex_lock_slow(pthread_mutex_t *);
     47 
     48 __strong_alias(__libc_mutex_init,pthread_mutex_init);
     49 __strong_alias(__libc_mutex_lock,pthread_mutex_lock);
     50 __strong_alias(__libc_mutex_trylock,pthread_mutex_trylock);
     51 __strong_alias(__libc_mutex_unlock,pthread_mutex_unlock);
     52 __strong_alias(__libc_mutex_destroy,pthread_mutex_destroy);
     53 
     54 __strong_alias(__libc_thr_once,pthread_once);
     55 
     56 int
     57 pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
     58 {
     59 
     60 #ifdef ERRORCHECK
     61 	if ((mutex == NULL) ||
     62 	    (attr && (attr->ptma_magic != _PT_MUTEXATTR_MAGIC)))
     63 		return EINVAL;
     64 #endif
     65 
     66 	mutex->ptm_magic = _PT_MUTEX_MAGIC;
     67 	mutex->ptm_owner = NULL;
     68 	pthread_lockinit(&mutex->ptm_lock);
     69 	pthread_lockinit(&mutex->ptm_interlock);
     70 	PTQ_INIT(&mutex->ptm_blocked);
     71 
     72 	return 0;
     73 }
     74 
     75 
     76 int
     77 pthread_mutex_destroy(pthread_mutex_t *mutex)
     78 {
     79 
     80 #ifdef ERRORCHECK
     81 	if ((mutex == NULL) ||
     82 	    (mutex->ptm_magic != _PT_MUTEX_MAGIC) ||
     83 	    (mutex->ptm_lock != __SIMPLELOCK_UNLOCKED))
     84 		return EINVAL;
     85 #endif
     86 
     87 	mutex->ptm_magic = _PT_MUTEX_DEAD;
     88 
     89 	return 0;
     90 }
     91 
     92 
     93 /*
     94  * Note regarding memory visibility: Pthreads has rules about memory
     95  * visibility and mutexes. Very roughly: Memory a thread can see when
     96  * it unlocks a mutex can be seen by another thread that locks the
     97  * same mutex.
     98  *
     99  * A memory barrier after a lock and before an unlock will provide
    100  * this behavior. This code relies on pthread__simple_lock_try() to issue
    101  * a barrier after obtaining a lock, and on pthread__simple_unlock() to
    102  * issue a barrier before releasing a lock.
    103  */
    104 
    105 int
    106 pthread_mutex_lock(pthread_mutex_t *mutex)
    107 {
    108 
    109 #ifdef ERRORCHECK
    110 	if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
    111 		return EINVAL;
    112 #endif
    113 
    114 	if (__predict_false(pthread__simple_lock_try(&mutex->ptm_lock) == 0))
    115 		pthread_mutex_lock_slow(mutex);
    116 
    117 	/* We have the lock! */
    118 #ifdef ERRORCHECK
    119 	mutex->ptm_owner = (pthread_t)pthread__sp();
    120 #endif
    121 	return 0;
    122 }
    123 
    124 static void
    125 pthread_mutex_lock_slow(pthread_mutex_t *mutex)
    126 {
    127 	pthread_t self;
    128 
    129 	self = pthread__self();
    130 
    131 	while (/*CONSTCOND*/1) {
    132 		if (pthread__simple_lock_try(&mutex->ptm_lock))
    133 		    break; /* got it! */
    134 
    135 		/* Okay, didn't look free. Get the interlock... */
    136 		pthread_spinlock(self, &mutex->ptm_interlock);
    137 		/*
    138 		 * The mutex_unlock routine will get the interlock
    139 		 * before looking at the list of sleepers, so if the
    140 		 * lock is held we can safely put ourselves on the
    141 		 * sleep queue. If it's not held, we can try taking it
    142 		 * again.
    143 		 */
    144 		if (mutex->ptm_lock == __SIMPLELOCK_LOCKED) {
    145 			PTQ_INSERT_TAIL(&mutex->ptm_blocked, self, pt_sleep);
    146 			/*
    147 			 * Locking a mutex is not a cancellation
    148 			 * point, so we don't need to do the
    149 			 * test-cancellation dance. We may get woken
    150 			 * up spuriously by pthread_cancel, though,
    151 			 * but it's okay since we're just going to
    152 			 * retry.
    153 			 */
    154 			pthread_spinlock(self, &self->pt_statelock);
    155 			self->pt_state = PT_STATE_BLOCKED_QUEUE;
    156 			self->pt_sleepobj = mutex;
    157 			self->pt_sleepq = &mutex->ptm_blocked;
    158 			self->pt_sleeplock = &mutex->ptm_interlock;
    159 			pthread_spinunlock(self, &self->pt_statelock);
    160 
    161 			pthread__block(self, &mutex->ptm_interlock);
    162 			/* interlock is not held when we return */
    163 		} else {
    164 			pthread_spinunlock(self, &mutex->ptm_interlock);
    165 		}
    166 		/* Go around for another try. */
    167 	}
    168 }
    169 
    170 
    171 int
    172 pthread_mutex_trylock(pthread_mutex_t *mutex)
    173 {
    174 
    175 #ifdef ERRORCHECK
    176 	if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
    177 		return EINVAL;
    178 #endif
    179 
    180 	if (pthread__simple_lock_try(&mutex->ptm_lock) == 0)
    181 		return EBUSY;
    182 
    183 #ifdef ERRORCHECK
    184 	mutex->ptm_owner = (pthread_t)pthread__sp();
    185 #endif
    186 	return 0;
    187 }
    188 
    189 
    190 int
    191 pthread_mutex_unlock(pthread_mutex_t *mutex)
    192 {
    193 	pthread_t self, blocked;
    194 
    195 	self = pthread__self();
    196 
    197 #ifdef ERRORCHECK
    198 	if ((mutex == NULL) || (mutex->ptm_magic != _PT_MUTEX_MAGIC))
    199 		return EINVAL;
    200 
    201 	if (mutex->ptm_lock != __SIMPLELOCK_LOCKED)
    202 		return EPERM; /* Not exactly the right error. */
    203 #endif
    204 
    205 	pthread_spinlock(self, &mutex->ptm_interlock);
    206 	blocked = PTQ_FIRST(&mutex->ptm_blocked);
    207 	if (blocked)
    208 		PTQ_REMOVE(&mutex->ptm_blocked, blocked, pt_sleep);
    209 #ifdef ERRORCHECK
    210 	mutex->ptm_owner = NULL;
    211 #endif
    212 	pthread__simple_unlock(&mutex->ptm_lock);
    213 	pthread_spinunlock(self, &mutex->ptm_interlock);
    214 
    215 	/* Give the head of the blocked queue another try. */
    216 	if (blocked)
    217 		pthread__sched(self, blocked);
    218 
    219 	return 0;
    220 }
    221 
    222 int
    223 pthread_mutexattr_init(pthread_mutexattr_t *attr)
    224 {
    225 
    226 #ifdef ERRORCHECK
    227 	if (attr == NULL)
    228 		return EINVAL;
    229 #endif
    230 
    231 	attr->ptma_magic = _PT_MUTEXATTR_MAGIC;
    232 
    233 	return 0;
    234 }
    235 
    236 
    237 int
    238 pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
    239 {
    240 
    241 #ifdef ERRORCHECK
    242 	if ((attr == NULL) ||
    243 	    (attr->ptma_magic != _PT_MUTEXATTR_MAGIC))
    244 		return EINVAL;
    245 #endif
    246 
    247 	attr->ptma_magic = _PT_MUTEXATTR_DEAD;
    248 
    249 	return 0;
    250 }
    251 
    252 
    253 int
    254 pthread_once(pthread_once_t *once_control, void (*routine)(void))
    255 {
    256 
    257 	if (once_control->pto_done == 0) {
    258 		pthread_mutex_lock(&once_control->pto_mutex);
    259 		if (once_control->pto_done == 0) {
    260 			routine();
    261 			once_control->pto_done = 1;
    262 		}
    263 		pthread_mutex_unlock(&once_control->pto_mutex);
    264 	}
    265 
    266 	return 0;
    267 }
    268