Home | History | Annotate | Line # | Download | only in libpthread
pthread_rwlock.c revision 1.18
      1 /*	$NetBSD: pthread_rwlock.c,v 1.18 2007/03/24 18:52:00 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.18 2007/03/24 18:52:00 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 		(void)pthread__park(self, &rwlock->ptr_interlock,
    121 		    &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked);
    122 	}
    123 
    124 	rwlock->ptr_nreaders++;
    125 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    126 
    127 	return 0;
    128 }
    129 
    130 
    131 int
    132 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
    133 {
    134 	pthread_t self;
    135 #ifdef ERRORCHECK
    136 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    137 		return EINVAL;
    138 #endif
    139 	self = pthread__self();
    140 
    141 	pthread_spinlock(self, &rwlock->ptr_interlock);
    142 	/*
    143 	 * Don't get a readlock if there is a writer or if there are waiting
    144 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    145 	 * by SUSv3.
    146 	 */
    147 	if ((rwlock->ptr_writer != NULL) ||
    148 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    149 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    150 		return EBUSY;
    151 	}
    152 
    153 	rwlock->ptr_nreaders++;
    154 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    155 
    156 	return 0;
    157 }
    158 
    159 
    160 int
    161 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
    162 {
    163 	pthread_t self;
    164 	extern int pthread__started;
    165 
    166 #ifdef ERRORCHECK
    167 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    168 		return EINVAL;
    169 #endif
    170 	self = pthread__self();
    171 
    172 	pthread_spinlock(self, &rwlock->ptr_interlock);
    173 #ifdef ERRORCHECK
    174 	if (rwlock->ptr_writer == self) {
    175 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    176 		return EDEADLK;
    177 	}
    178 #endif
    179 	/*
    180 	 * Prefer writers to readers here; permit writers even if there are
    181 	 * waiting readers.
    182 	 */
    183 	while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    184 #ifdef ERRORCHECK
    185 		if (pthread__started == 0) {
    186 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    187 			return EDEADLK;
    188 		}
    189 #endif
    190 	    	PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
    191 		self->pt_sleeponq = 1;
    192 		self->pt_sleepobj = &rwlock->ptr_wblocked;
    193 		(void)pthread__park(self, &rwlock->ptr_interlock,
    194 		    &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked);
    195 	}
    196 
    197 	rwlock->ptr_writer = self;
    198 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    199 
    200 	return 0;
    201 }
    202 
    203 
    204 int
    205 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
    206 {
    207 	pthread_t self;
    208 #ifdef ERRORCHECK
    209 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    210 		return EINVAL;
    211 #endif
    212 	self = pthread__self();
    213 
    214 	pthread_spinlock(self, &rwlock->ptr_interlock);
    215 	/*
    216 	 * Prefer writers to readers here; permit writers even if there are
    217 	 * waiting readers.
    218 	 */
    219 	if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    220 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    221 		return EBUSY;
    222 	}
    223 
    224 	rwlock->ptr_writer = self;
    225 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    226 
    227 	return 0;
    228 }
    229 
    230 
    231 struct pthread_rwlock__waitarg {
    232 	pthread_t ptw_thread;
    233 	pthread_rwlock_t *ptw_rwlock;
    234 	struct pthread_queue_t *ptw_queue;
    235 };
    236 
    237 int
    238 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
    239 	    const struct timespec *abs_timeout)
    240 {
    241 	pthread_t self;
    242 	int retval;
    243 
    244 #ifdef ERRORCHECK
    245 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    246 		return EINVAL;
    247 	if (abs_timeout == NULL)
    248 		return EINVAL;
    249 #endif
    250 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    251 	    (abs_timeout->tv_nsec < 0) ||
    252 	    (abs_timeout->tv_sec < 0))
    253 		return EINVAL;
    254 
    255 	self = pthread__self();
    256 	pthread_spinlock(self, &rwlock->ptr_interlock);
    257 #ifdef ERRORCHECK
    258 	if (rwlock->ptr_writer == self) {
    259 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    260 		return EDEADLK;
    261 	}
    262 #endif
    263 	/*
    264 	 * Don't get a readlock if there is a writer or if there are waiting
    265 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    266 	 * by SUSv3.
    267 	 */
    268 	retval = 0;
    269 	while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
    270 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
    271 	    	PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
    272 		self->pt_sleeponq = 1;
    273 		self->pt_sleepobj = &rwlock->ptr_rblocked;
    274 		retval = pthread__park(self, &rwlock->ptr_interlock,
    275 		    &rwlock->ptr_rblocked, abs_timeout, 0,
    276 		    &rwlock->ptr_rblocked);
    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 		retval = pthread__park(self, &rwlock->ptr_interlock,
    337 		    &rwlock->ptr_wblocked, abs_timeout, 0,
    338 		    &rwlock->ptr_wblocked);
    339 	}
    340 
    341 	if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
    342 		rwlock->ptr_writer = self;
    343 		retval = 0;
    344 	}
    345 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    346 
    347 	return retval;
    348 }
    349 
    350 
    351 int
    352 pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
    353 {
    354 	pthread_t self, writer;
    355 #ifdef ERRORCHECK
    356 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    357 		return EINVAL;
    358 #endif
    359 	writer = NULL;
    360 	self = pthread__self();
    361 
    362 	pthread_spinlock(self, &rwlock->ptr_interlock);
    363 	if (rwlock->ptr_writer != NULL) {
    364 		/* Releasing a write lock. */
    365 #ifdef ERRORCHECK
    366 		if (rwlock->ptr_writer != self) {
    367 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    368 			return EPERM;
    369 		}
    370 #endif
    371 		rwlock->ptr_writer = NULL;
    372 		writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    373 		if (writer != NULL) {
    374 			PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
    375 		}
    376 	} else
    377 #ifdef ERRORCHECK
    378 	if (rwlock->ptr_nreaders > 0)
    379 #endif
    380 	{
    381 		/* Releasing a read lock. */
    382 		rwlock->ptr_nreaders--;
    383 		if (rwlock->ptr_nreaders == 0) {
    384 			writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    385 			if (writer != NULL)
    386 				PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
    387 				    pt_sleep);
    388 		}
    389 #ifdef ERRORCHECK
    390 	} else {
    391 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    392 		return EPERM;
    393 #endif
    394 	}
    395 
    396 	if (writer != NULL)
    397 		pthread__unpark(self, &rwlock->ptr_interlock,
    398 		    &rwlock->ptr_wblocked, writer);
    399 	else
    400 		pthread__unpark_all(self, &rwlock->ptr_interlock,
    401 		    &rwlock->ptr_rblocked);
    402 
    403 	return 0;
    404 }
    405 
    406 
    407 int
    408 pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
    409 {
    410 #ifdef ERRORCHECK
    411 	if (attr == NULL)
    412 		return EINVAL;
    413 #endif
    414 	attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
    415 
    416 	return 0;
    417 }
    418 
    419 
    420 int
    421 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
    422 {
    423 #ifdef ERRORCHECK
    424 	if ((attr == NULL) ||
    425 	    (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
    426 		return EINVAL;
    427 #endif
    428 	attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
    429 
    430 	return 0;
    431 }
    432