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