Home | History | Annotate | Line # | Download | only in libpthread
pthread_rwlock.c revision 1.13.6.1
      1  1.13.6.1  wrstuden /*	$NetBSD: pthread_rwlock.c,v 1.13.6.1 2007/09/10 05:24:53 wrstuden Exp $ */
      2       1.2   thorpej 
      3       1.2   thorpej /*-
      4       1.2   thorpej  * Copyright (c) 2002 The NetBSD Foundation, Inc.
      5       1.2   thorpej  * All rights reserved.
      6       1.2   thorpej  *
      7       1.2   thorpej  * This code is derived from software contributed to The NetBSD Foundation
      8       1.2   thorpej  * by Nathan J. Williams.
      9       1.2   thorpej  *
     10       1.2   thorpej  * Redistribution and use in source and binary forms, with or without
     11       1.2   thorpej  * modification, are permitted provided that the following conditions
     12       1.2   thorpej  * are met:
     13       1.2   thorpej  * 1. Redistributions of source code must retain the above copyright
     14       1.2   thorpej  *    notice, this list of conditions and the following disclaimer.
     15       1.2   thorpej  * 2. Redistributions in binary form must reproduce the above copyright
     16       1.2   thorpej  *    notice, this list of conditions and the following disclaimer in the
     17       1.2   thorpej  *    documentation and/or other materials provided with the distribution.
     18       1.2   thorpej  * 3. All advertising materials mentioning features or use of this software
     19       1.2   thorpej  *    must display the following acknowledgement:
     20       1.2   thorpej  *        This product includes software developed by the NetBSD
     21       1.2   thorpej  *        Foundation, Inc. and its contributors.
     22       1.2   thorpej  * 4. Neither the name of The NetBSD Foundation nor the names of its
     23       1.2   thorpej  *    contributors may be used to endorse or promote products derived
     24       1.2   thorpej  *    from this software without specific prior written permission.
     25       1.2   thorpej  *
     26       1.2   thorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     27       1.2   thorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     28       1.2   thorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     29       1.2   thorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     30       1.2   thorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     31       1.2   thorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     32       1.2   thorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     33       1.2   thorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     34       1.2   thorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     35       1.2   thorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     36       1.2   thorpej  * POSSIBILITY OF SUCH DAMAGE.
     37       1.2   thorpej  */
     38       1.2   thorpej 
     39       1.5     lukem #include <sys/cdefs.h>
     40  1.13.6.1  wrstuden __RCSID("$NetBSD: pthread_rwlock.c,v 1.13.6.1 2007/09/10 05:24:53 wrstuden Exp $");
     41       1.5     lukem 
     42       1.2   thorpej #include <errno.h>
     43       1.2   thorpej 
     44       1.2   thorpej #include "pthread.h"
     45       1.2   thorpej #include "pthread_int.h"
     46       1.2   thorpej 
     47       1.2   thorpej static void pthread_rwlock__callback(void *);
     48       1.2   thorpej 
     49       1.2   thorpej __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
     50       1.2   thorpej __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
     51       1.2   thorpej __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
     52       1.2   thorpej __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
     53       1.2   thorpej __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
     54       1.2   thorpej __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
     55       1.2   thorpej __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
     56       1.2   thorpej 
     57       1.2   thorpej int
     58       1.2   thorpej pthread_rwlock_init(pthread_rwlock_t *rwlock,
     59       1.2   thorpej 	    const pthread_rwlockattr_t *attr)
     60       1.2   thorpej {
     61       1.2   thorpej #ifdef ERRORCHECK
     62       1.2   thorpej 	if ((rwlock == NULL) ||
     63       1.2   thorpej 	    (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
     64       1.2   thorpej 		return EINVAL;
     65       1.2   thorpej #endif
     66       1.2   thorpej 	rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
     67       1.2   thorpej 	pthread_lockinit(&rwlock->ptr_interlock);
     68       1.2   thorpej 	PTQ_INIT(&rwlock->ptr_rblocked);
     69       1.2   thorpej 	PTQ_INIT(&rwlock->ptr_wblocked);
     70       1.2   thorpej 	rwlock->ptr_nreaders = 0;
     71       1.2   thorpej 	rwlock->ptr_writer = NULL;
     72       1.2   thorpej 
     73       1.2   thorpej 	return 0;
     74       1.2   thorpej }
     75       1.2   thorpej 
     76       1.2   thorpej 
     77       1.2   thorpej int
     78       1.2   thorpej pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
     79       1.2   thorpej {
     80       1.2   thorpej #ifdef ERRORCHECK
     81       1.2   thorpej 	if ((rwlock == NULL) ||
     82       1.2   thorpej 	    (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
     83       1.2   thorpej 	    (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
     84       1.2   thorpej 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
     85       1.2   thorpej 	    (rwlock->ptr_nreaders != 0) ||
     86       1.2   thorpej 	    (rwlock->ptr_writer != NULL))
     87       1.2   thorpej 		return EINVAL;
     88       1.2   thorpej #endif
     89       1.2   thorpej 	rwlock->ptr_magic = _PT_RWLOCK_DEAD;
     90       1.2   thorpej 
     91       1.2   thorpej 	return 0;
     92       1.2   thorpej }
     93       1.2   thorpej 
     94       1.2   thorpej 
     95       1.2   thorpej int
     96       1.2   thorpej pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
     97       1.2   thorpej {
     98       1.2   thorpej 	pthread_t self;
     99       1.2   thorpej #ifdef ERRORCHECK
    100       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    101       1.2   thorpej 		return EINVAL;
    102       1.2   thorpej #endif
    103       1.2   thorpej 	self = pthread__self();
    104       1.2   thorpej 
    105       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    106       1.2   thorpej #ifdef ERRORCHECK
    107       1.2   thorpej 	if (rwlock->ptr_writer == self) {
    108       1.2   thorpej 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    109       1.2   thorpej 		return EDEADLK;
    110       1.2   thorpej 	}
    111       1.2   thorpej #endif
    112       1.2   thorpej 	/*
    113       1.2   thorpej 	 * Don't get a readlock if there is a writer or if there are waiting
    114       1.2   thorpej 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    115       1.2   thorpej 	 * by SUSv3.
    116       1.2   thorpej 	 */
    117       1.2   thorpej 	while ((rwlock->ptr_writer != NULL) ||
    118       1.2   thorpej 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    119       1.2   thorpej 		PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
    120       1.2   thorpej 		/* Locking a rwlock is not a cancellation point; don't check */
    121       1.2   thorpej 		pthread_spinlock(self, &self->pt_statelock);
    122  1.13.6.1  wrstuden 		if (pthread_check_defsig(self)) {
    123  1.13.6.1  wrstuden 			pthread_spinunlock(self, &self->pt_statelock);
    124  1.13.6.1  wrstuden 			PTQ_REMOVE(&rwlock->ptr_rblocked, self, pt_sleep);
    125  1.13.6.1  wrstuden 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    126  1.13.6.1  wrstuden 			pthread__signal_deferred(self, self);
    127  1.13.6.1  wrstuden 			pthread_spinlock(self, &rwlock->ptr_interlock);
    128  1.13.6.1  wrstuden 			continue;
    129  1.13.6.1  wrstuden 		}
    130       1.2   thorpej 		self->pt_state = PT_STATE_BLOCKED_QUEUE;
    131       1.2   thorpej 		self->pt_sleepobj = rwlock;
    132       1.2   thorpej 		self->pt_sleepq = &rwlock->ptr_rblocked;
    133       1.2   thorpej 		self->pt_sleeplock = &rwlock->ptr_interlock;
    134       1.2   thorpej 		pthread_spinunlock(self, &self->pt_statelock);
    135       1.2   thorpej 		pthread__block(self, &rwlock->ptr_interlock);
    136       1.2   thorpej 		/* interlock is not held when we return */
    137       1.2   thorpej 		pthread_spinlock(self, &rwlock->ptr_interlock);
    138       1.2   thorpej 	}
    139       1.2   thorpej 
    140       1.2   thorpej 	rwlock->ptr_nreaders++;
    141       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    142       1.2   thorpej 
    143       1.2   thorpej 	return 0;
    144       1.2   thorpej }
    145       1.2   thorpej 
    146       1.2   thorpej 
    147       1.2   thorpej int
    148       1.2   thorpej pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
    149       1.2   thorpej {
    150       1.2   thorpej 	pthread_t self;
    151       1.2   thorpej #ifdef ERRORCHECK
    152       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    153       1.2   thorpej 		return EINVAL;
    154       1.2   thorpej #endif
    155       1.2   thorpej 	self = pthread__self();
    156       1.2   thorpej 
    157       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    158       1.2   thorpej 	/*
    159       1.2   thorpej 	 * Don't get a readlock if there is a writer or if there are waiting
    160       1.2   thorpej 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    161       1.2   thorpej 	 * by SUSv3.
    162       1.2   thorpej 	 */
    163       1.2   thorpej 	if ((rwlock->ptr_writer != NULL) ||
    164       1.2   thorpej 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    165       1.2   thorpej 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    166       1.2   thorpej 		return EBUSY;
    167       1.2   thorpej 	}
    168       1.2   thorpej 
    169       1.2   thorpej 	rwlock->ptr_nreaders++;
    170       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    171       1.2   thorpej 
    172       1.2   thorpej 	return 0;
    173       1.2   thorpej }
    174       1.2   thorpej 
    175       1.2   thorpej 
    176       1.2   thorpej int
    177       1.2   thorpej pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
    178       1.2   thorpej {
    179       1.2   thorpej 	pthread_t self;
    180      1.13       chs 	extern int pthread__started;
    181      1.13       chs 
    182       1.2   thorpej #ifdef ERRORCHECK
    183       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    184       1.2   thorpej 		return EINVAL;
    185       1.2   thorpej #endif
    186       1.2   thorpej 	self = pthread__self();
    187       1.2   thorpej 
    188       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    189       1.7        cl #ifdef ERRORCHECK
    190       1.7        cl 	if (rwlock->ptr_writer == self) {
    191       1.7        cl 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    192       1.7        cl 		return EDEADLK;
    193       1.7        cl 	}
    194       1.7        cl #endif
    195       1.2   thorpej 	/*
    196       1.2   thorpej 	 * Prefer writers to readers here; permit writers even if there are
    197       1.2   thorpej 	 * waiting readers.
    198       1.2   thorpej 	 */
    199       1.2   thorpej 	while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    200      1.13       chs #ifdef ERRORCHECK
    201      1.13       chs 		if (pthread__started == 0) {
    202      1.13       chs 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    203      1.13       chs 			return EDEADLK;
    204      1.13       chs 		}
    205      1.13       chs #endif
    206       1.2   thorpej 		PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
    207       1.2   thorpej 		/* Locking a rwlock is not a cancellation point; don't check */
    208       1.2   thorpej 		pthread_spinlock(self, &self->pt_statelock);
    209  1.13.6.1  wrstuden 		if (pthread_check_defsig(self)) {
    210  1.13.6.1  wrstuden 			pthread_spinunlock(self, &self->pt_statelock);
    211  1.13.6.1  wrstuden 			PTQ_REMOVE(&rwlock->ptr_wblocked, self, pt_sleep);
    212  1.13.6.1  wrstuden 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    213  1.13.6.1  wrstuden 			pthread__signal_deferred(self, self);
    214  1.13.6.1  wrstuden 			pthread_spinlock(self, &rwlock->ptr_interlock);
    215  1.13.6.1  wrstuden 			continue;
    216  1.13.6.1  wrstuden 		}
    217       1.2   thorpej 		self->pt_state = PT_STATE_BLOCKED_QUEUE;
    218       1.2   thorpej 		self->pt_sleepobj = rwlock;
    219       1.2   thorpej 		self->pt_sleepq = &rwlock->ptr_wblocked;
    220       1.2   thorpej 		self->pt_sleeplock = &rwlock->ptr_interlock;
    221       1.2   thorpej 		pthread_spinunlock(self, &self->pt_statelock);
    222       1.2   thorpej 		pthread__block(self, &rwlock->ptr_interlock);
    223       1.2   thorpej 		/* interlock is not held when we return */
    224       1.2   thorpej 		pthread_spinlock(self, &rwlock->ptr_interlock);
    225       1.2   thorpej 	}
    226       1.2   thorpej 
    227       1.2   thorpej 	rwlock->ptr_writer = self;
    228       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    229       1.2   thorpej 
    230       1.2   thorpej 	return 0;
    231       1.2   thorpej }
    232       1.2   thorpej 
    233       1.2   thorpej 
    234       1.2   thorpej int
    235       1.2   thorpej pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
    236       1.2   thorpej {
    237       1.2   thorpej 	pthread_t self;
    238       1.2   thorpej #ifdef ERRORCHECK
    239       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    240       1.2   thorpej 		return EINVAL;
    241       1.2   thorpej #endif
    242       1.2   thorpej 	self = pthread__self();
    243       1.2   thorpej 
    244       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    245       1.2   thorpej 	/*
    246       1.2   thorpej 	 * Prefer writers to readers here; permit writers even if there are
    247       1.2   thorpej 	 * waiting readers.
    248       1.2   thorpej 	 */
    249       1.2   thorpej 	if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
    250       1.2   thorpej 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    251       1.2   thorpej 		return EBUSY;
    252       1.2   thorpej 	}
    253       1.2   thorpej 
    254       1.2   thorpej 	rwlock->ptr_writer = self;
    255       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    256       1.2   thorpej 
    257       1.2   thorpej 	return 0;
    258       1.2   thorpej }
    259       1.2   thorpej 
    260       1.2   thorpej 
    261       1.2   thorpej struct pthread_rwlock__waitarg {
    262       1.2   thorpej 	pthread_t ptw_thread;
    263       1.2   thorpej 	pthread_rwlock_t *ptw_rwlock;
    264       1.2   thorpej 	struct pthread_queue_t *ptw_queue;
    265       1.2   thorpej };
    266       1.2   thorpej 
    267       1.2   thorpej int
    268       1.2   thorpej pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
    269       1.2   thorpej 	    const struct timespec *abs_timeout)
    270       1.2   thorpej {
    271       1.2   thorpej 	pthread_t self;
    272       1.2   thorpej 	struct pthread_rwlock__waitarg wait;
    273       1.2   thorpej 	struct pt_alarm_t alarm;
    274       1.2   thorpej 	int retval;
    275      1.12       chs 
    276       1.2   thorpej #ifdef ERRORCHECK
    277       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    278       1.2   thorpej 		return EINVAL;
    279      1.10   nathanw 	if (abs_timeout == NULL)
    280       1.2   thorpej 		return EINVAL;
    281       1.2   thorpej #endif
    282      1.10   nathanw 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    283      1.10   nathanw 	    (abs_timeout->tv_nsec < 0) ||
    284      1.10   nathanw 	    (abs_timeout->tv_sec < 0))
    285      1.10   nathanw 		return EINVAL;
    286      1.12       chs 
    287       1.2   thorpej 	self = pthread__self();
    288       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    289       1.2   thorpej #ifdef ERRORCHECK
    290       1.2   thorpej 	if (rwlock->ptr_writer == self) {
    291       1.9      yamt 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    292       1.2   thorpej 		return EDEADLK;
    293       1.2   thorpej 	}
    294       1.2   thorpej #endif
    295       1.2   thorpej 	/*
    296       1.2   thorpej 	 * Don't get a readlock if there is a writer or if there are waiting
    297       1.2   thorpej 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    298       1.2   thorpej 	 * by SUSv3.
    299       1.2   thorpej 	 */
    300       1.2   thorpej 	retval = 0;
    301       1.2   thorpej 	while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
    302       1.2   thorpej 	    (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
    303       1.2   thorpej 		wait.ptw_thread = self;
    304       1.2   thorpej 		wait.ptw_rwlock = rwlock;
    305       1.2   thorpej 		wait.ptw_queue = &rwlock->ptr_rblocked;
    306  1.13.6.1  wrstuden 		/* Locking a rwlock is not a cancellation point; don't check */
    307  1.13.6.1  wrstuden 		pthread_spinlock(self, &self->pt_statelock);
    308  1.13.6.1  wrstuden 		if (pthread_check_defsig(self)) {
    309  1.13.6.1  wrstuden 			pthread_spinunlock(self, &self->pt_statelock);
    310  1.13.6.1  wrstuden 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    311  1.13.6.1  wrstuden 			pthread__signal_deferred(self, self);
    312  1.13.6.1  wrstuden 			pthread_spinlock(self, &rwlock->ptr_interlock);
    313  1.13.6.1  wrstuden 			continue;
    314  1.13.6.1  wrstuden 		}
    315       1.2   thorpej 		pthread__alarm_add(self, &alarm, abs_timeout,
    316       1.2   thorpej 		    pthread_rwlock__callback, &wait);
    317       1.2   thorpej 		PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
    318       1.2   thorpej 		self->pt_state = PT_STATE_BLOCKED_QUEUE;
    319       1.2   thorpej 		self->pt_sleepobj = rwlock;
    320       1.2   thorpej 		self->pt_sleepq = &rwlock->ptr_rblocked;
    321       1.2   thorpej 		self->pt_sleeplock = &rwlock->ptr_interlock;
    322       1.2   thorpej 		pthread_spinunlock(self, &self->pt_statelock);
    323       1.2   thorpej 		pthread__block(self, &rwlock->ptr_interlock);
    324       1.2   thorpej 		/* interlock is not held when we return */
    325       1.2   thorpej 		pthread__alarm_del(self, &alarm);
    326       1.2   thorpej 		if (pthread__alarm_fired(&alarm))
    327       1.2   thorpej 			retval = ETIMEDOUT;
    328       1.2   thorpej 		pthread_spinlock(self, &rwlock->ptr_interlock);
    329       1.2   thorpej 	}
    330       1.2   thorpej 
    331      1.11   nathanw 	/* One last chance to get the lock, in case it was released between
    332      1.11   nathanw 	   the alarm firing and when this thread got rescheduled, or in case
    333      1.11   nathanw 	   a signal handler kept it busy */
    334      1.11   nathanw 	if ((rwlock->ptr_writer == NULL) &&
    335      1.11   nathanw 	    (PTQ_EMPTY(&rwlock->ptr_wblocked))) {
    336       1.2   thorpej 		rwlock->ptr_nreaders++;
    337      1.11   nathanw 		retval = 0;
    338      1.11   nathanw 	}
    339       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    340       1.2   thorpej 
    341       1.2   thorpej 	return retval;
    342       1.2   thorpej }
    343       1.2   thorpej 
    344       1.2   thorpej 
    345       1.2   thorpej int
    346       1.2   thorpej pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
    347       1.2   thorpej 	    const struct timespec *abs_timeout)
    348       1.2   thorpej {
    349       1.2   thorpej 	struct pthread_rwlock__waitarg wait;
    350       1.2   thorpej 	struct pt_alarm_t alarm;
    351      1.12       chs 	pthread_t self;
    352       1.2   thorpej 	int retval;
    353      1.12       chs 	extern int pthread__started;
    354      1.12       chs 
    355       1.2   thorpej #ifdef ERRORCHECK
    356       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    357       1.2   thorpej 		return EINVAL;
    358      1.10   nathanw 	if (abs_timeout == NULL)
    359      1.10   nathanw 		return EINVAL;
    360       1.2   thorpej #endif
    361      1.10   nathanw 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    362      1.10   nathanw 	    (abs_timeout->tv_nsec < 0) ||
    363      1.10   nathanw 	    (abs_timeout->tv_sec < 0))
    364      1.10   nathanw 		return EINVAL;
    365      1.12       chs 
    366       1.2   thorpej 	self = pthread__self();
    367       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    368       1.7        cl #ifdef ERRORCHECK
    369       1.7        cl 	if (rwlock->ptr_writer == self) {
    370       1.9      yamt 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    371       1.7        cl 		return EDEADLK;
    372       1.7        cl 	}
    373       1.7        cl #endif
    374       1.2   thorpej 	/*
    375       1.2   thorpej 	 * Prefer writers to readers here; permit writers even if there are
    376       1.2   thorpej 	 * waiting readers.
    377       1.2   thorpej 	 */
    378       1.2   thorpej 	retval = 0;
    379       1.2   thorpej 	while (retval == 0 &&
    380       1.2   thorpej 	    ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
    381      1.13       chs #ifdef ERRORCHECK
    382      1.13       chs 		if (pthread__started == 0) {
    383      1.13       chs 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    384      1.13       chs 			return EDEADLK;
    385      1.13       chs 		}
    386      1.13       chs #endif
    387       1.2   thorpej 		wait.ptw_thread = self;
    388       1.2   thorpej 		wait.ptw_rwlock = rwlock;
    389       1.2   thorpej 		wait.ptw_queue = &rwlock->ptr_wblocked;
    390  1.13.6.1  wrstuden 		/* Locking a rwlock is not a cancellation point; don't check */
    391  1.13.6.1  wrstuden 		pthread_spinlock(self, &self->pt_statelock);
    392  1.13.6.1  wrstuden 		if (pthread_check_defsig(self)) {
    393  1.13.6.1  wrstuden 			pthread_spinunlock(self, &self->pt_statelock);
    394  1.13.6.1  wrstuden 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    395  1.13.6.1  wrstuden 			pthread__signal_deferred(self, self);
    396  1.13.6.1  wrstuden 			pthread_spinlock(self, &rwlock->ptr_interlock);
    397  1.13.6.1  wrstuden 			continue;
    398  1.13.6.1  wrstuden 		}
    399       1.2   thorpej 		pthread__alarm_add(self, &alarm, abs_timeout,
    400       1.2   thorpej 		    pthread_rwlock__callback, &wait);
    401       1.2   thorpej 		PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
    402       1.2   thorpej 		self->pt_state = PT_STATE_BLOCKED_QUEUE;
    403       1.2   thorpej 		self->pt_sleepobj = rwlock;
    404       1.2   thorpej 		self->pt_sleepq = &rwlock->ptr_wblocked;
    405       1.2   thorpej 		self->pt_sleeplock = &rwlock->ptr_interlock;
    406       1.2   thorpej 		pthread_spinunlock(self, &self->pt_statelock);
    407       1.2   thorpej 		pthread__block(self, &rwlock->ptr_interlock);
    408       1.2   thorpej 		/* interlock is not held when we return */
    409       1.2   thorpej 		pthread__alarm_del(self, &alarm);
    410       1.2   thorpej 		if (pthread__alarm_fired(&alarm))
    411       1.2   thorpej 			retval = ETIMEDOUT;
    412       1.2   thorpej 		pthread_spinlock(self, &rwlock->ptr_interlock);
    413       1.2   thorpej 	}
    414       1.2   thorpej 
    415      1.11   nathanw 	if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
    416       1.2   thorpej 		rwlock->ptr_writer = self;
    417      1.11   nathanw 		retval = 0;
    418      1.11   nathanw 	}
    419       1.2   thorpej 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    420       1.2   thorpej 
    421       1.8      yamt 	return retval;
    422       1.2   thorpej }
    423       1.2   thorpej 
    424       1.2   thorpej 
    425       1.2   thorpej static void
    426       1.2   thorpej pthread_rwlock__callback(void *arg)
    427       1.2   thorpej {
    428       1.2   thorpej 	struct pthread_rwlock__waitarg *a;
    429       1.2   thorpej 	pthread_t self;
    430       1.2   thorpej 
    431       1.2   thorpej 	a = arg;
    432       1.2   thorpej 	self = pthread__self();
    433       1.2   thorpej 
    434       1.2   thorpej 	pthread_spinlock(self, &a->ptw_rwlock->ptr_interlock);
    435       1.2   thorpej 	/*
    436       1.2   thorpej 	 * Don't dequeue and schedule the thread if it's already been
    437       1.2   thorpej 	 * queued up by a signal or broadcast (but hasn't yet run as far
    438       1.2   thorpej 	 * as pthread__alarm_del(), or we wouldn't be here, and hence can't
    439       1.2   thorpej 	 * have become blocked on some *other* queue).
    440       1.2   thorpej 	 */
    441       1.2   thorpej 	if (a->ptw_thread->pt_state == PT_STATE_BLOCKED_QUEUE) {
    442       1.2   thorpej 		PTQ_REMOVE(a->ptw_queue, a->ptw_thread, pt_sleep);
    443       1.2   thorpej 		pthread__sched(self, a->ptw_thread);
    444       1.2   thorpej 	}
    445       1.2   thorpej 	pthread_spinunlock(self, &a->ptw_rwlock->ptr_interlock);
    446       1.2   thorpej 
    447       1.2   thorpej }
    448       1.2   thorpej 
    449       1.2   thorpej 
    450       1.2   thorpej int
    451       1.2   thorpej pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
    452       1.2   thorpej {
    453       1.3   nathanw 	pthread_t self, writer;
    454       1.2   thorpej 	struct pthread_queue_t blockedq;
    455       1.2   thorpej #ifdef ERRORCHECK
    456       1.2   thorpej 	if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
    457       1.2   thorpej 		return EINVAL;
    458       1.2   thorpej #endif
    459       1.2   thorpej 	writer = NULL;
    460       1.2   thorpej 	PTQ_INIT(&blockedq);
    461       1.2   thorpej 	self = pthread__self();
    462       1.2   thorpej 
    463       1.2   thorpej 	pthread_spinlock(self, &rwlock->ptr_interlock);
    464       1.2   thorpej 	if (rwlock->ptr_writer != NULL) {
    465       1.2   thorpej 		/* Releasing a write lock. */
    466       1.2   thorpej #ifdef ERRORCHECK
    467       1.2   thorpej 		if (rwlock->ptr_writer != self) {
    468       1.2   thorpej 			pthread_spinunlock(self, &rwlock->ptr_interlock);
    469       1.2   thorpej 			return EPERM;
    470       1.2   thorpej 		}
    471       1.2   thorpej #endif
    472       1.2   thorpej 		rwlock->ptr_writer = NULL;
    473       1.2   thorpej 		writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    474       1.2   thorpej 		if (writer != NULL) {
    475       1.2   thorpej 			PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
    476       1.2   thorpej 		} else {
    477       1.2   thorpej 			blockedq = rwlock->ptr_rblocked;
    478       1.2   thorpej 			PTQ_INIT(&rwlock->ptr_rblocked);
    479       1.2   thorpej 		}
    480       1.7        cl 	} else
    481       1.7        cl #ifdef ERRORCHECK
    482       1.7        cl 	if (rwlock->ptr_nreaders > 0)
    483       1.7        cl #endif
    484       1.7        cl 	{
    485       1.2   thorpej 		/* Releasing a read lock. */
    486       1.2   thorpej 		rwlock->ptr_nreaders--;
    487       1.2   thorpej 		if (rwlock->ptr_nreaders == 0) {
    488       1.2   thorpej 			writer = PTQ_FIRST(&rwlock->ptr_wblocked);
    489       1.2   thorpej 			if (writer != NULL)
    490       1.2   thorpej 				PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
    491       1.2   thorpej 				    pt_sleep);
    492       1.2   thorpej 		}
    493       1.7        cl #ifdef ERRORCHECK
    494       1.7        cl 	} else {
    495       1.7        cl 		pthread_spinunlock(self, &rwlock->ptr_interlock);
    496       1.7        cl 		return EPERM;
    497       1.7        cl #endif
    498       1.2   thorpej 	}
    499       1.2   thorpej 
    500       1.2   thorpej 	if (writer != NULL)
    501       1.2   thorpej 		pthread__sched(self, writer);
    502       1.2   thorpej 	else
    503       1.3   nathanw 		pthread__sched_sleepers(self, &blockedq);
    504       1.2   thorpej 
    505       1.6        cl 	pthread_spinunlock(self, &rwlock->ptr_interlock);
    506       1.6        cl 
    507       1.2   thorpej 	return 0;
    508       1.2   thorpej }
    509       1.2   thorpej 
    510       1.2   thorpej 
    511       1.2   thorpej int
    512       1.2   thorpej pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
    513       1.2   thorpej {
    514       1.2   thorpej #ifdef ERRORCHECK
    515       1.2   thorpej 	if (attr == NULL)
    516       1.2   thorpej 		return EINVAL;
    517       1.2   thorpej #endif
    518       1.2   thorpej 	attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
    519       1.2   thorpej 
    520       1.2   thorpej 	return 0;
    521       1.2   thorpej }
    522       1.2   thorpej 
    523       1.2   thorpej 
    524       1.2   thorpej int
    525       1.2   thorpej pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
    526       1.2   thorpej {
    527       1.2   thorpej #ifdef ERRORCHECK
    528       1.2   thorpej 	if ((attr == NULL) ||
    529       1.2   thorpej 	    (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
    530       1.2   thorpej 		return EINVAL;
    531       1.2   thorpej #endif
    532       1.2   thorpej 	attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
    533       1.2   thorpej 
    534       1.2   thorpej 	return 0;
    535       1.2   thorpej }
    536