Home | History | Annotate | Line # | Download | only in libpthread
pthread_rwlock.c revision 1.25
      1 /*	$NetBSD: pthread_rwlock.c,v 1.25 2008/01/31 10:07:09 mlelstv Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2002, 2006, 2007 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 and Andrew Doran.
      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 <sys/cdefs.h>
     40 __RCSID("$NetBSD: pthread_rwlock.c,v 1.25 2008/01/31 10:07:09 mlelstv Exp $");
     41 
     42 #include <errno.h>
     43 
     44 #include "pthread.h"
     45 #include "pthread_int.h"
     46 
     47 #ifndef	PTHREAD__HAVE_ATOMIC
     48 
     49 __weak_alias(pthread_rwlock_held_np,_pthread_rwlock_held_np)
     50 __weak_alias(pthread_rwlock_rdheld_np,_pthread_rwlock_rdheld_np)
     51 __weak_alias(pthread_rwlock_wrheld_np,_pthread_rwlock_wrheld_np)
     52 
     53 int	_pthread_rwlock_held_np(pthread_rwlock_t *);
     54 int	_pthread_rwlock_rdheld_np(pthread_rwlock_t *);
     55 int	_pthread_rwlock_wrheld_np(pthread_rwlock_t *);
     56 
     57 __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
     58 __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
     59 __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
     60 __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
     61 __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
     62 __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
     63 __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
     64 
     65 int
     66 pthread_rwlock_init(pthread_rwlock_t *rwlock,
     67 	    const pthread_rwlockattr_t *attr)
     68 {
     69 #ifdef ERRORCHECK
     70 	if ((rwlock == NULL) ||
     71 	    (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
     72 		return EINVAL;
     73 #endif
     74 	rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
     75 	pthread_lockinit(&rwlock->ptr_interlock);
     76 	PTQ_INIT(&rwlock->ptr_rblocked);
     77 	PTQ_INIT(&rwlock->ptr_wblocked);
     78 	rwlock->ptr_nreaders = 0;
     79 	rwlock->ptr_writer = NULL;
     80 
     81 	return 0;
     82 }
     83 
     84 
     85 int
     86 pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
     87 {
     88 #ifdef ERRORCHECK
     89 	if ((rwlock == NULL) ||
     90 	    (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
     91 	    (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
     92 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
     93 	    (rwlock->ptr_nreaders != 0) ||
     94 	    (rwlock->ptr_writer != NULL))
     95 		return EINVAL;
     96 #endif
     97 	rwlock->ptr_magic = _PT_RWLOCK_DEAD;
     98 
     99 	return 0;
    100 }
    101 
    102 
    103 int
    104 pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
    105 {
    106 	pthread_t self;
    107 #ifdef ERRORCHECK
    108 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    109 		return EINVAL;
    110 #endif
    111 	self = pthread__self();
    112 
    113 	pthread__spinlock(self, &rwlock->ptr_interlock);
    114 #ifdef ERRORCHECK
    115 	if (rwlock->ptr_writer == self) {
    116 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    117 		return EDEADLK;
    118 	}
    119 #endif
    120 	/*
    121 	 * Don't get a readlock if there is a writer or if there are waiting
    122 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    123 	 * by SUSv3.
    124 	 */
    125 	while ((rwlock->ptr_writer != NULL) ||
    126 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    127 	    	PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
    128 		self->pt_sleeponq = 1;
    129 		self->pt_sleepobj = &rwlock->ptr_rblocked;
    130 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    131 		(void)pthread__park(self, &rwlock->ptr_interlock,
    132 		    &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked);
    133 		pthread__spinlock(self, &rwlock->ptr_interlock);
    134 	}
    135 
    136 	rwlock->ptr_nreaders++;
    137 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    138 
    139 	return 0;
    140 }
    141 
    142 
    143 int
    144 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
    145 {
    146 	pthread_t self;
    147 
    148 #ifdef ERRORCHECK
    149 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    150 		return EINVAL;
    151 #endif
    152 
    153 	self = pthread__self();
    154 	pthread__spinlock(self, &rwlock->ptr_interlock);
    155 	/*
    156 	 * Don't get a readlock if there is a writer or if there are waiting
    157 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    158 	 * by SUSv3.
    159 	 */
    160 	if ((rwlock->ptr_writer != NULL) ||
    161 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    162 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    163 		return EBUSY;
    164 	}
    165 
    166 	rwlock->ptr_nreaders++;
    167 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    168 
    169 	return 0;
    170 }
    171 
    172 
    173 int
    174 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
    175 {
    176 	pthread_t self;
    177 	extern int pthread__started;
    178 
    179 #ifdef ERRORCHECK
    180 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    181 		return EINVAL;
    182 #endif
    183 	self = pthread__self();
    184 
    185 	pthread__spinlock(self, &rwlock->ptr_interlock);
    186 #ifdef ERRORCHECK
    187 	if (rwlock->ptr_writer == self) {
    188 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    189 		return EDEADLK;
    190 	}
    191 #endif
    192 	/*
    193 	 * Prefer writers to readers here; permit writers even if there are
    194 	 * waiting readers.
    195 	 */
    196 	while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    197 #ifdef ERRORCHECK
    198 		if (pthread__started == 0) {
    199 			pthread__spinunlock(self, &rwlock->ptr_interlock);
    200 			return EDEADLK;
    201 		}
    202 #endif
    203 	    	PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
    204 		self->pt_sleeponq = 1;
    205 		self->pt_sleepobj = &rwlock->ptr_wblocked;
    206 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    207 		(void)pthread__park(self, &rwlock->ptr_interlock,
    208 		    &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked);
    209 		pthread__spinlock(self, &rwlock->ptr_interlock);
    210 	}
    211 
    212 	rwlock->ptr_writer = self;
    213 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    214 
    215 	return 0;
    216 }
    217 
    218 
    219 int
    220 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
    221 {
    222 	pthread_t self;
    223 #ifdef ERRORCHECK
    224 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    225 		return EINVAL;
    226 #endif
    227 	self = pthread__self();
    228 
    229 	pthread__spinlock(self, &rwlock->ptr_interlock);
    230 	/*
    231 	 * Prefer writers to readers here; permit writers even if there are
    232 	 * waiting readers.
    233 	 */
    234 	if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    235 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    236 		return EBUSY;
    237 	}
    238 
    239 	rwlock->ptr_writer = self;
    240 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    241 
    242 	return 0;
    243 }
    244 
    245 
    246 int
    247 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
    248 	    const struct timespec *abs_timeout)
    249 {
    250 	pthread_t self;
    251 	int retval;
    252 
    253 #ifdef ERRORCHECK
    254 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    255 		return EINVAL;
    256 	if (abs_timeout == NULL)
    257 		return EINVAL;
    258 #endif
    259 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    260 	    (abs_timeout->tv_nsec < 0) ||
    261 	    (abs_timeout->tv_sec < 0))
    262 		return EINVAL;
    263 
    264 	self = pthread__self();
    265 	pthread__spinlock(self, &rwlock->ptr_interlock);
    266 #ifdef ERRORCHECK
    267 	if (rwlock->ptr_writer == self) {
    268 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    269 		return EDEADLK;
    270 	}
    271 #endif
    272 	/*
    273 	 * Don't get a readlock if there is a writer or if there are waiting
    274 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    275 	 * by SUSv3.
    276 	 */
    277 	retval = 0;
    278 	while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
    279 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
    280 	    	PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
    281 		self->pt_sleeponq = 1;
    282 		self->pt_sleepobj = &rwlock->ptr_rblocked;
    283 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    284 		retval = pthread__park(self, &rwlock->ptr_interlock,
    285 		    &rwlock->ptr_rblocked, abs_timeout, 0,
    286 		    &rwlock->ptr_rblocked);
    287 		pthread__spinlock(self, &rwlock->ptr_interlock);
    288 	}
    289 
    290 	/* One last chance to get the lock, in case it was released between
    291 	   the alarm firing and when this thread got rescheduled, or in case
    292 	   a signal handler kept it busy */
    293 	if ((rwlock->ptr_writer == NULL) &&
    294 	    (PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    295 		rwlock->ptr_nreaders++;
    296 		retval = 0;
    297 	}
    298 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    299 
    300 	return retval;
    301 }
    302 
    303 
    304 int
    305 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
    306 	    const struct timespec *abs_timeout)
    307 {
    308 	pthread_t self;
    309 	int retval;
    310 	extern int pthread__started;
    311 
    312 #ifdef ERRORCHECK
    313 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    314 		return EINVAL;
    315 	if (abs_timeout == NULL)
    316 		return EINVAL;
    317 #endif
    318 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    319 	    (abs_timeout->tv_nsec < 0) ||
    320 	    (abs_timeout->tv_sec < 0))
    321 		return EINVAL;
    322 
    323 	self = pthread__self();
    324 	pthread__spinlock(self, &rwlock->ptr_interlock);
    325 #ifdef ERRORCHECK
    326 	if (rwlock->ptr_writer == self) {
    327 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    328 		return EDEADLK;
    329 	}
    330 #endif
    331 	/*
    332 	 * Prefer writers to readers here; permit writers even if there are
    333 	 * waiting readers.
    334 	 */
    335 	retval = 0;
    336 	while (retval == 0 &&
    337 	    ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
    338 #ifdef ERRORCHECK
    339 		if (pthread__started == 0) {
    340 			pthread__spinunlock(self, &rwlock->ptr_interlock);
    341 			return EDEADLK;
    342 		}
    343 #endif
    344 	    	PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
    345 		self->pt_sleeponq = 1;
    346 		self->pt_sleepobj = &rwlock->ptr_wblocked;
    347 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    348 		retval = pthread__park(self, &rwlock->ptr_interlock,
    349 		    &rwlock->ptr_wblocked, abs_timeout, 0,
    350 		    &rwlock->ptr_wblocked);
    351 		pthread__spinlock(self, &rwlock->ptr_interlock);
    352 	}
    353 
    354 	if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
    355 		rwlock->ptr_writer = self;
    356 		retval = 0;
    357 	}
    358 	pthread__spinunlock(self, &rwlock->ptr_interlock);
    359 
    360 	return retval;
    361 }
    362 
    363 
    364 int
    365 pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
    366 {
    367 	pthread_t self, writer;
    368 #ifdef ERRORCHECK
    369 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    370 		return EINVAL;
    371 #endif
    372 	writer = NULL;
    373 	self = pthread__self();
    374 
    375 	pthread__spinlock(self, &rwlock->ptr_interlock);
    376 	if (rwlock->ptr_writer != NULL) {
    377 		/* Releasing a write lock. */
    378 #ifdef ERRORCHECK
    379 		if (rwlock->ptr_writer != self) {
    380 			pthread__spinunlock(self, &rwlock->ptr_interlock);
    381 			return EPERM;
    382 		}
    383 #endif
    384 		rwlock->ptr_writer = NULL;
    385 		writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    386 		if (writer != NULL) {
    387 			PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
    388 		}
    389 	} else
    390 #ifdef ERRORCHECK
    391 	if (rwlock->ptr_nreaders > 0)
    392 #endif
    393 	{
    394 		/* Releasing a read lock. */
    395 		rwlock->ptr_nreaders--;
    396 		if (rwlock->ptr_nreaders == 0) {
    397 			writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    398 			if (writer != NULL)
    399 				PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
    400 				    pt_sleep);
    401 		}
    402 #ifdef ERRORCHECK
    403 	} else {
    404 		pthread__spinunlock(self, &rwlock->ptr_interlock);
    405 		return EPERM;
    406 #endif
    407 	}
    408 
    409 	if (writer != NULL)
    410 		pthread__unpark(self, &rwlock->ptr_interlock,
    411 		    &rwlock->ptr_wblocked, writer);
    412 	else
    413 		pthread__unpark_all(self, &rwlock->ptr_interlock,
    414 		    &rwlock->ptr_rblocked);
    415 
    416 	return 0;
    417 }
    418 
    419 
    420 int
    421 pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
    422 {
    423 #ifdef ERRORCHECK
    424 	if (attr == NULL)
    425 		return EINVAL;
    426 #endif
    427 	attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
    428 
    429 	return 0;
    430 }
    431 
    432 
    433 int
    434 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
    435 {
    436 #ifdef ERRORCHECK
    437 	if ((attr == NULL) ||
    438 	    (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
    439 		return EINVAL;
    440 #endif
    441 	attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
    442 
    443 	return 0;
    444 }
    445 
    446 int
    447 _pthread_rwlock_held_np(pthread_rwlock_t *ptr)
    448 {
    449 
    450 	return ptr->ptr_writer != NULL || ptr->ptr_nreaders != 0;
    451 }
    452 
    453 int
    454 _pthread_rwlock_rdheld_np(pthread_rwlock_t *ptr)
    455 {
    456 
    457 	return ptr->ptr_nreaders != 0;
    458 }
    459 
    460 int
    461 _pthread_rwlock_wrheld_np(pthread_rwlock_t *ptr)
    462 {
    463 
    464 	return ptr->ptr_writer != 0;
    465 }
    466 
    467 #endif	/* !PTHREAD__HAVE_ATOMIC */
    468