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