Home | History | Annotate | Line # | Download | only in libpthread
pthread_rwlock.c revision 1.29.2.1
      1  1.29.2.1  wrstuden /*	$NetBSD: pthread_rwlock.c,v 1.29.2.1 2008/06/23 04:29:54 wrstuden Exp $ */
      2       1.2   thorpej 
      3       1.2   thorpej /*-
      4      1.27        ad  * Copyright (c) 2002, 2006, 2007, 2008 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.27        ad  * by Nathan J. Williams, by Jason R. Thorpe, and by Andrew Doran.
      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  *
     19       1.2   thorpej  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20       1.2   thorpej  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21       1.2   thorpej  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22       1.2   thorpej  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23       1.2   thorpej  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24       1.2   thorpej  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25       1.2   thorpej  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26       1.2   thorpej  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27       1.2   thorpej  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28       1.2   thorpej  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29       1.2   thorpej  * POSSIBILITY OF SUCH DAMAGE.
     30       1.2   thorpej  */
     31       1.2   thorpej 
     32       1.5     lukem #include <sys/cdefs.h>
     33  1.29.2.1  wrstuden __RCSID("$NetBSD: pthread_rwlock.c,v 1.29.2.1 2008/06/23 04:29:54 wrstuden Exp $");
     34  1.29.2.1  wrstuden 
     35  1.29.2.1  wrstuden #include <sys/types.h>
     36  1.29.2.1  wrstuden #include <sys/lwpctl.h>
     37       1.5     lukem 
     38       1.2   thorpej #include <errno.h>
     39      1.27        ad #include <stddef.h>
     40       1.2   thorpej 
     41       1.2   thorpej #include "pthread.h"
     42       1.2   thorpej #include "pthread_int.h"
     43       1.2   thorpej 
     44      1.27        ad #define	_RW_LOCKED		0
     45      1.27        ad #define	_RW_WANT_WRITE		1
     46      1.27        ad #define	_RW_WANT_READ		2
     47      1.27        ad 
     48  1.29.2.1  wrstuden #if __GNUC_PREREQ__(3, 0)
     49  1.29.2.1  wrstuden #define	NOINLINE		__attribute ((noinline))
     50  1.29.2.1  wrstuden #else
     51  1.29.2.1  wrstuden #define	NOINLINE		/* nothing */
     52  1.29.2.1  wrstuden #endif
     53  1.29.2.1  wrstuden 
     54      1.27        ad static int pthread__rwlock_wrlock(pthread_rwlock_t *, const struct timespec *);
     55      1.27        ad static int pthread__rwlock_rdlock(pthread_rwlock_t *, const struct timespec *);
     56      1.27        ad static void pthread__rwlock_early(void *);
     57      1.24  christos 
     58      1.23        ad int	_pthread_rwlock_held_np(pthread_rwlock_t *);
     59      1.23        ad int	_pthread_rwlock_rdheld_np(pthread_rwlock_t *);
     60      1.23        ad int	_pthread_rwlock_wrheld_np(pthread_rwlock_t *);
     61      1.23        ad 
     62      1.27        ad #ifndef lint
     63      1.27        ad __weak_alias(pthread_rwlock_held_np,_pthread_rwlock_held_np);
     64      1.27        ad __weak_alias(pthread_rwlock_rdheld_np,_pthread_rwlock_rdheld_np);
     65      1.27        ad __weak_alias(pthread_rwlock_wrheld_np,_pthread_rwlock_wrheld_np);
     66      1.27        ad #endif
     67      1.27        ad 
     68       1.2   thorpej __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
     69       1.2   thorpej __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
     70       1.2   thorpej __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
     71       1.2   thorpej __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
     72       1.2   thorpej __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
     73       1.2   thorpej __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
     74       1.2   thorpej __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
     75       1.2   thorpej 
     76      1.27        ad static inline uintptr_t
     77      1.27        ad rw_cas(pthread_rwlock_t *ptr, uintptr_t o, uintptr_t n)
     78      1.27        ad {
     79      1.27        ad 
     80      1.27        ad 	return (uintptr_t)atomic_cas_ptr(&ptr->ptr_owner, (void *)o,
     81      1.27        ad 	    (void *)n);
     82      1.27        ad }
     83      1.27        ad 
     84       1.2   thorpej int
     85      1.27        ad pthread_rwlock_init(pthread_rwlock_t *ptr,
     86       1.2   thorpej 	    const pthread_rwlockattr_t *attr)
     87       1.2   thorpej {
     88      1.27        ad 
     89      1.27        ad 	if (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
     90       1.2   thorpej 		return EINVAL;
     91      1.27        ad 	ptr->ptr_magic = _PT_RWLOCK_MAGIC;
     92      1.27        ad 	PTQ_INIT(&ptr->ptr_rblocked);
     93      1.27        ad 	PTQ_INIT(&ptr->ptr_wblocked);
     94      1.27        ad 	ptr->ptr_nreaders = 0;
     95      1.27        ad 	ptr->ptr_owner = NULL;
     96       1.2   thorpej 
     97       1.2   thorpej 	return 0;
     98       1.2   thorpej }
     99       1.2   thorpej 
    100       1.2   thorpej 
    101       1.2   thorpej int
    102      1.27        ad pthread_rwlock_destroy(pthread_rwlock_t *ptr)
    103       1.2   thorpej {
    104      1.27        ad 
    105      1.27        ad 	if ((ptr->ptr_magic != _PT_RWLOCK_MAGIC) ||
    106      1.27        ad 	    (!PTQ_EMPTY(&ptr->ptr_rblocked)) ||
    107      1.27        ad 	    (!PTQ_EMPTY(&ptr->ptr_wblocked)) ||
    108      1.27        ad 	    (ptr->ptr_nreaders != 0) ||
    109      1.27        ad 	    (ptr->ptr_owner != NULL))
    110       1.2   thorpej 		return EINVAL;
    111      1.27        ad 	ptr->ptr_magic = _PT_RWLOCK_DEAD;
    112       1.2   thorpej 
    113       1.2   thorpej 	return 0;
    114       1.2   thorpej }
    115       1.2   thorpej 
    116  1.29.2.1  wrstuden /* We want function call overhead. */
    117  1.29.2.1  wrstuden NOINLINE static void
    118  1.29.2.1  wrstuden pthread__rwlock_pause(void)
    119  1.29.2.1  wrstuden {
    120  1.29.2.1  wrstuden 
    121  1.29.2.1  wrstuden 	pthread__smt_pause();
    122  1.29.2.1  wrstuden }
    123  1.29.2.1  wrstuden 
    124  1.29.2.1  wrstuden NOINLINE static int
    125  1.29.2.1  wrstuden pthread__rwlock_spin(uintptr_t owner)
    126  1.29.2.1  wrstuden {
    127  1.29.2.1  wrstuden 	pthread_t thread;
    128  1.29.2.1  wrstuden 	unsigned int i;
    129  1.29.2.1  wrstuden 
    130  1.29.2.1  wrstuden 	thread = (pthread_t)(owner & RW_THREAD);
    131  1.29.2.1  wrstuden 	if (thread == NULL || (owner & ~RW_THREAD) != RW_WRITE_LOCKED)
    132  1.29.2.1  wrstuden 		return 0;
    133  1.29.2.1  wrstuden 	if (thread->pt_lwpctl->lc_curcpu == LWPCTL_CPU_NONE ||
    134  1.29.2.1  wrstuden 	    thread->pt_blocking)
    135  1.29.2.1  wrstuden 		return 0;
    136  1.29.2.1  wrstuden 	for (i = 128; i != 0; i--)
    137  1.29.2.1  wrstuden 		pthread__rwlock_pause();
    138  1.29.2.1  wrstuden 	return 1;
    139  1.29.2.1  wrstuden }
    140  1.29.2.1  wrstuden 
    141      1.27        ad static int
    142      1.27        ad pthread__rwlock_rdlock(pthread_rwlock_t *ptr, const struct timespec *ts)
    143       1.2   thorpej {
    144      1.27        ad 	uintptr_t owner, next;
    145  1.29.2.1  wrstuden 	pthread_mutex_t *interlock;
    146       1.2   thorpej 	pthread_t self;
    147      1.27        ad 	int error;
    148      1.27        ad 
    149      1.27        ad 	self = pthread__self();
    150      1.27        ad 
    151       1.2   thorpej #ifdef ERRORCHECK
    152      1.27        ad 	if (ptr->ptr_magic != _PT_RWLOCK_MAGIC)
    153       1.2   thorpej 		return EINVAL;
    154       1.2   thorpej #endif
    155      1.27        ad 
    156      1.27        ad 	for (owner = (uintptr_t)ptr->ptr_owner;; owner = next) {
    157      1.27        ad 		/*
    158      1.27        ad 		 * Read the lock owner field.  If the need-to-wait
    159      1.27        ad 		 * indicator is clear, then try to acquire the lock.
    160      1.27        ad 		 */
    161      1.27        ad 		if ((owner & (RW_WRITE_LOCKED | RW_WRITE_WANTED)) == 0) {
    162      1.27        ad 			next = rw_cas(ptr, owner, owner + RW_READ_INCR);
    163      1.27        ad 			if (owner == next) {
    164      1.27        ad 				/* Got it! */
    165      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    166      1.27        ad 				membar_enter();
    167      1.27        ad #endif
    168      1.27        ad 				return 0;
    169      1.27        ad 			}
    170      1.27        ad 
    171      1.27        ad 			/*
    172      1.27        ad 			 * Didn't get it -- spin around again (we'll
    173      1.27        ad 			 * probably sleep on the next iteration).
    174      1.27        ad 			 */
    175      1.27        ad 			continue;
    176      1.27        ad 		}
    177      1.27        ad 
    178      1.27        ad 		if ((owner & RW_THREAD) == (uintptr_t)self)
    179      1.27        ad 			return EDEADLK;
    180      1.27        ad 
    181  1.29.2.1  wrstuden 		/* If held write locked and no waiters, spin. */
    182  1.29.2.1  wrstuden 		if (pthread__rwlock_spin(owner)) {
    183  1.29.2.1  wrstuden 			while (pthread__rwlock_spin(owner)) {
    184  1.29.2.1  wrstuden 				owner = (uintptr_t)ptr->ptr_owner;
    185  1.29.2.1  wrstuden 			}
    186  1.29.2.1  wrstuden 			next = owner;
    187  1.29.2.1  wrstuden 			continue;
    188  1.29.2.1  wrstuden 		}
    189  1.29.2.1  wrstuden 
    190      1.27        ad 		/*
    191      1.27        ad 		 * Grab the interlock.  Once we have that, we
    192      1.27        ad 		 * can adjust the waiter bits and sleep queue.
    193      1.27        ad 		 */
    194  1.29.2.1  wrstuden 		interlock = pthread__hashlock(ptr);
    195  1.29.2.1  wrstuden 		pthread_mutex_lock(interlock);
    196      1.27        ad 
    197      1.27        ad 		/*
    198      1.27        ad 		 * Mark the rwlock as having waiters.  If the set fails,
    199      1.27        ad 		 * then we may not need to sleep and should spin again.
    200      1.27        ad 		 */
    201      1.27        ad 		next = rw_cas(ptr, owner, owner | RW_HAS_WAITERS);
    202      1.27        ad 		if (owner != next) {
    203  1.29.2.1  wrstuden 			pthread_mutex_unlock(interlock);
    204      1.27        ad 			continue;
    205      1.27        ad 		}
    206      1.27        ad 
    207      1.27        ad 		/* The waiters bit is set - it's safe to sleep. */
    208      1.27        ad 	    	PTQ_INSERT_HEAD(&ptr->ptr_rblocked, self, pt_sleep);
    209      1.27        ad 	    	ptr->ptr_nreaders++;
    210      1.27        ad 		self->pt_rwlocked = _RW_WANT_READ;
    211      1.27        ad 		self->pt_sleepobj = &ptr->ptr_rblocked;
    212      1.27        ad 		self->pt_early = pthread__rwlock_early;
    213  1.29.2.1  wrstuden 		error = pthread__park(self, interlock, &ptr->ptr_rblocked,
    214  1.29.2.1  wrstuden 		    ts, 0, &ptr->ptr_rblocked);
    215      1.27        ad 
    216      1.27        ad 		/* Did we get the lock? */
    217      1.27        ad 		if (self->pt_rwlocked == _RW_LOCKED) {
    218      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    219      1.27        ad 			membar_enter();
    220       1.2   thorpej #endif
    221      1.27        ad 			return 0;
    222      1.27        ad 		}
    223      1.27        ad 		if (error != 0)
    224      1.27        ad 			return error;
    225      1.27        ad 
    226      1.27        ad 		pthread__errorfunc(__FILE__, __LINE__, __func__,
    227      1.27        ad 		    "direct handoff failure");
    228       1.2   thorpej 	}
    229       1.2   thorpej }
    230       1.2   thorpej 
    231       1.2   thorpej 
    232       1.2   thorpej int
    233      1.27        ad pthread_rwlock_tryrdlock(pthread_rwlock_t *ptr)
    234       1.2   thorpej {
    235      1.27        ad 	uintptr_t owner, next;
    236      1.20        ad 
    237       1.2   thorpej #ifdef ERRORCHECK
    238      1.27        ad 	if (ptr->ptr_magic != _PT_RWLOCK_MAGIC)
    239       1.2   thorpej 		return EINVAL;
    240       1.2   thorpej #endif
    241      1.27        ad 
    242       1.2   thorpej 	/*
    243       1.2   thorpej 	 * Don't get a readlock if there is a writer or if there are waiting
    244       1.2   thorpej 	 * writers; i.e. prefer writers to readers. This strategy is dictated
    245       1.2   thorpej 	 * by SUSv3.
    246       1.2   thorpej 	 */
    247      1.27        ad 	for (owner = (uintptr_t)ptr->ptr_owner;; owner = next) {
    248      1.27        ad 		if ((owner & (RW_WRITE_LOCKED | RW_WRITE_WANTED)) != 0)
    249      1.27        ad 			return EBUSY;
    250      1.27        ad 		next = rw_cas(ptr, owner, owner + RW_READ_INCR);
    251      1.27        ad 		if (owner == next) {
    252      1.27        ad 			/* Got it! */
    253      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    254      1.27        ad 			membar_enter();
    255      1.27        ad #endif
    256      1.27        ad 			return 0;
    257      1.27        ad 		}
    258       1.2   thorpej 	}
    259       1.2   thorpej }
    260       1.2   thorpej 
    261      1.27        ad static int
    262      1.27        ad pthread__rwlock_wrlock(pthread_rwlock_t *ptr, const struct timespec *ts)
    263       1.2   thorpej {
    264      1.27        ad 	uintptr_t owner, next;
    265  1.29.2.1  wrstuden 	pthread_mutex_t *interlock;
    266       1.2   thorpej 	pthread_t self;
    267      1.27        ad 	int error;
    268      1.27        ad 
    269      1.27        ad 	self = pthread__self();
    270      1.13       chs 
    271       1.2   thorpej #ifdef ERRORCHECK
    272      1.27        ad 	if (ptr->ptr_magic != _PT_RWLOCK_MAGIC)
    273       1.2   thorpej 		return EINVAL;
    274       1.2   thorpej #endif
    275      1.27        ad 
    276      1.27        ad 	for (owner = (uintptr_t)ptr->ptr_owner;; owner = next) {
    277      1.27        ad 		/*
    278      1.27        ad 		 * Read the lock owner field.  If the need-to-wait
    279      1.27        ad 		 * indicator is clear, then try to acquire the lock.
    280      1.27        ad 		 */
    281      1.27        ad 		if ((owner & RW_THREAD) == 0) {
    282      1.27        ad 			next = rw_cas(ptr, owner,
    283      1.27        ad 			    (uintptr_t)self | RW_WRITE_LOCKED);
    284      1.27        ad 			if (owner == next) {
    285      1.27        ad 				/* Got it! */
    286      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    287      1.27        ad 				membar_enter();
    288      1.27        ad #endif
    289      1.27        ad 				return 0;
    290      1.27        ad 			}
    291      1.27        ad 
    292      1.27        ad 			/*
    293      1.27        ad 			 * Didn't get it -- spin around again (we'll
    294      1.27        ad 			 * probably sleep on the next iteration).
    295      1.27        ad 			 */
    296      1.27        ad 			continue;
    297      1.27        ad 		}
    298      1.27        ad 
    299      1.27        ad 		if ((owner & RW_THREAD) == (uintptr_t)self)
    300      1.13       chs 			return EDEADLK;
    301      1.27        ad 
    302  1.29.2.1  wrstuden 		/* If held write locked and no waiters, spin. */
    303  1.29.2.1  wrstuden 		if (pthread__rwlock_spin(owner)) {
    304  1.29.2.1  wrstuden 			while (pthread__rwlock_spin(owner)) {
    305  1.29.2.1  wrstuden 				owner = (uintptr_t)ptr->ptr_owner;
    306  1.29.2.1  wrstuden 			}
    307  1.29.2.1  wrstuden 			next = owner;
    308  1.29.2.1  wrstuden 			continue;
    309  1.29.2.1  wrstuden 		}
    310  1.29.2.1  wrstuden 
    311      1.27        ad 		/*
    312      1.27        ad 		 * Grab the interlock.  Once we have that, we
    313      1.27        ad 		 * can adjust the waiter bits and sleep queue.
    314      1.27        ad 		 */
    315  1.29.2.1  wrstuden 		interlock = pthread__hashlock(ptr);
    316  1.29.2.1  wrstuden 		pthread_mutex_lock(interlock);
    317      1.27        ad 
    318      1.27        ad 		/*
    319      1.27        ad 		 * Mark the rwlock as having waiters.  If the set fails,
    320      1.27        ad 		 * then we may not need to sleep and should spin again.
    321      1.27        ad 		 */
    322      1.27        ad 		next = rw_cas(ptr, owner,
    323      1.27        ad 		    owner | RW_HAS_WAITERS | RW_WRITE_WANTED);
    324      1.27        ad 		if (owner != next) {
    325  1.29.2.1  wrstuden 			pthread_mutex_unlock(interlock);
    326      1.27        ad 			continue;
    327      1.13       chs 		}
    328      1.27        ad 
    329      1.27        ad 		/* The waiters bit is set - it's safe to sleep. */
    330      1.27        ad 	    	PTQ_INSERT_TAIL(&ptr->ptr_wblocked, self, pt_sleep);
    331      1.27        ad 		self->pt_rwlocked = _RW_WANT_WRITE;
    332      1.27        ad 		self->pt_sleepobj = &ptr->ptr_wblocked;
    333      1.27        ad 		self->pt_early = pthread__rwlock_early;
    334  1.29.2.1  wrstuden 		error = pthread__park(self, interlock, &ptr->ptr_wblocked,
    335  1.29.2.1  wrstuden 		    ts, 0, &ptr->ptr_wblocked);
    336      1.27        ad 
    337      1.27        ad 		/* Did we get the lock? */
    338      1.27        ad 		if (self->pt_rwlocked == _RW_LOCKED) {
    339      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    340      1.27        ad 			membar_enter();
    341      1.13       chs #endif
    342      1.27        ad 			return 0;
    343      1.27        ad 		}
    344      1.27        ad 		if (error != 0)
    345      1.27        ad 			return error;
    346      1.27        ad 
    347      1.27        ad 		pthread__errorfunc(__FILE__, __LINE__, __func__,
    348      1.27        ad 		    "direct handoff failure");
    349       1.2   thorpej 	}
    350       1.2   thorpej }
    351       1.2   thorpej 
    352       1.2   thorpej 
    353       1.2   thorpej int
    354      1.27        ad pthread_rwlock_trywrlock(pthread_rwlock_t *ptr)
    355       1.2   thorpej {
    356      1.27        ad 	uintptr_t owner, next;
    357       1.2   thorpej 	pthread_t self;
    358      1.27        ad 
    359       1.2   thorpej #ifdef ERRORCHECK
    360      1.27        ad 	if (ptr->ptr_magic != _PT_RWLOCK_MAGIC)
    361       1.2   thorpej 		return EINVAL;
    362       1.2   thorpej #endif
    363      1.27        ad 
    364       1.2   thorpej 	self = pthread__self();
    365      1.27        ad 
    366      1.27        ad 	for (owner = (uintptr_t)ptr->ptr_owner;; owner = next) {
    367      1.27        ad 		if (owner != 0)
    368      1.27        ad 			return EBUSY;
    369      1.27        ad 		next = rw_cas(ptr, owner, (uintptr_t)self | RW_WRITE_LOCKED);
    370      1.27        ad 		if (owner == next) {
    371      1.27        ad 			/* Got it! */
    372      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    373      1.27        ad 			membar_enter();
    374      1.27        ad #endif
    375      1.27        ad 			return 0;
    376      1.27        ad 		}
    377       1.2   thorpej 	}
    378      1.27        ad }
    379       1.2   thorpej 
    380      1.27        ad int
    381      1.27        ad pthread_rwlock_rdlock(pthread_rwlock_t *ptr)
    382      1.27        ad {
    383       1.2   thorpej 
    384      1.27        ad 	return pthread__rwlock_rdlock(ptr, NULL);
    385       1.2   thorpej }
    386       1.2   thorpej 
    387       1.2   thorpej int
    388      1.27        ad pthread_rwlock_timedrdlock(pthread_rwlock_t *ptr,
    389      1.27        ad 			   const struct timespec *abs_timeout)
    390       1.2   thorpej {
    391      1.12       chs 
    392      1.10   nathanw 	if (abs_timeout == NULL)
    393       1.2   thorpej 		return EINVAL;
    394      1.10   nathanw 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    395      1.10   nathanw 	    (abs_timeout->tv_nsec < 0) ||
    396      1.10   nathanw 	    (abs_timeout->tv_sec < 0))
    397      1.10   nathanw 		return EINVAL;
    398      1.12       chs 
    399      1.27        ad 	return pthread__rwlock_rdlock(ptr, abs_timeout);
    400      1.27        ad }
    401       1.2   thorpej 
    402      1.27        ad int
    403      1.27        ad pthread_rwlock_wrlock(pthread_rwlock_t *ptr)
    404      1.27        ad {
    405       1.2   thorpej 
    406      1.27        ad 	return pthread__rwlock_wrlock(ptr, NULL);
    407       1.2   thorpej }
    408       1.2   thorpej 
    409       1.2   thorpej int
    410      1.27        ad pthread_rwlock_timedwrlock(pthread_rwlock_t *ptr,
    411      1.27        ad 			   const struct timespec *abs_timeout)
    412       1.2   thorpej {
    413      1.12       chs 
    414      1.10   nathanw 	if (abs_timeout == NULL)
    415      1.10   nathanw 		return EINVAL;
    416      1.10   nathanw 	if ((abs_timeout->tv_nsec >= 1000000000) ||
    417      1.10   nathanw 	    (abs_timeout->tv_nsec < 0) ||
    418      1.10   nathanw 	    (abs_timeout->tv_sec < 0))
    419      1.10   nathanw 		return EINVAL;
    420      1.12       chs 
    421      1.27        ad 	return pthread__rwlock_wrlock(ptr, abs_timeout);
    422       1.2   thorpej }
    423       1.2   thorpej 
    424       1.2   thorpej 
    425       1.2   thorpej int
    426      1.27        ad pthread_rwlock_unlock(pthread_rwlock_t *ptr)
    427       1.2   thorpej {
    428      1.27        ad 	uintptr_t owner, decr, new, next;
    429  1.29.2.1  wrstuden 	pthread_mutex_t *interlock;
    430      1.27        ad 	pthread_t self, thread;
    431      1.27        ad 
    432       1.2   thorpej #ifdef ERRORCHECK
    433      1.27        ad 	if ((ptr == NULL) || (ptr->ptr_magic != _PT_RWLOCK_MAGIC))
    434       1.2   thorpej 		return EINVAL;
    435       1.2   thorpej #endif
    436      1.27        ad 
    437      1.27        ad #ifndef PTHREAD__ATOMIC_IS_MEMBAR
    438      1.27        ad 	membar_exit();
    439      1.27        ad #endif
    440      1.27        ad 
    441      1.27        ad 	/*
    442      1.27        ad 	 * Since we used an add operation to set the required lock
    443      1.27        ad 	 * bits, we can use a subtract to clear them, which makes
    444      1.27        ad 	 * the read-release and write-release path similar.
    445      1.27        ad 	 */
    446       1.2   thorpej 	self = pthread__self();
    447      1.27        ad 	owner = (uintptr_t)ptr->ptr_owner;
    448      1.27        ad 	if ((owner & RW_WRITE_LOCKED) != 0) {
    449      1.27        ad 		decr = (uintptr_t)self | RW_WRITE_LOCKED;
    450      1.27        ad 		if ((owner & RW_THREAD) != (uintptr_t)self) {
    451      1.27        ad 			return EPERM;
    452      1.27        ad 		}
    453      1.27        ad 	} else {
    454      1.27        ad 		decr = RW_READ_INCR;
    455      1.27        ad 		if (owner == 0) {
    456       1.2   thorpej 			return EPERM;
    457       1.2   thorpej 		}
    458      1.27        ad 	}
    459      1.27        ad 
    460      1.27        ad 	for (;; owner = next) {
    461      1.27        ad 		/*
    462      1.27        ad 		 * Compute what we expect the new value of the lock to be.
    463      1.27        ad 		 * Only proceed to do direct handoff if there are waiters,
    464      1.27        ad 		 * and if the lock would become unowned.
    465      1.27        ad 		 */
    466      1.27        ad 		new = (owner - decr);
    467      1.27        ad 		if ((new & (RW_THREAD | RW_HAS_WAITERS)) != RW_HAS_WAITERS) {
    468      1.27        ad 			next = rw_cas(ptr, owner, new);
    469      1.27        ad 			if (owner == next) {
    470      1.27        ad 				/* Released! */
    471      1.27        ad 				return 0;
    472      1.27        ad 			}
    473      1.27        ad 			continue;
    474      1.27        ad 		}
    475      1.27        ad 
    476      1.27        ad 		/*
    477      1.27        ad 		 * Grab the interlock.  Once we have that, we can adjust
    478      1.27        ad 		 * the waiter bits.  We must check to see if there are
    479      1.27        ad 		 * still waiters before proceeding.
    480      1.27        ad 		 */
    481  1.29.2.1  wrstuden 		interlock = pthread__hashlock(ptr);
    482  1.29.2.1  wrstuden 		pthread_mutex_lock(interlock);
    483      1.27        ad 		owner = (uintptr_t)ptr->ptr_owner;
    484      1.27        ad 		if ((owner & RW_HAS_WAITERS) == 0) {
    485  1.29.2.1  wrstuden 			pthread_mutex_unlock(interlock);
    486      1.27        ad 			next = owner;
    487      1.27        ad 			continue;
    488       1.2   thorpej 		}
    489      1.27        ad 
    490      1.27        ad 		/*
    491      1.27        ad 		 * Give the lock away.  SUSv3 dictates that we must give
    492      1.27        ad 		 * preference to writers.
    493      1.27        ad 		 */
    494      1.27        ad 		if ((thread = PTQ_FIRST(&ptr->ptr_wblocked)) != NULL) {
    495      1.27        ad 			new = (uintptr_t)thread | RW_WRITE_LOCKED;
    496      1.27        ad 
    497      1.27        ad 			if (PTQ_NEXT(thread, pt_sleep) != NULL)
    498      1.27        ad 				new |= RW_HAS_WAITERS | RW_WRITE_WANTED;
    499      1.27        ad 			else if (ptr->ptr_nreaders != 0)
    500      1.27        ad 				new |= RW_HAS_WAITERS;
    501      1.27        ad 
    502      1.27        ad 			/*
    503      1.27        ad 			 * Set in the new value.  The lock becomes owned
    504      1.27        ad 			 * by the writer that we are about to wake.
    505      1.27        ad 			 */
    506      1.27        ad 			(void)atomic_swap_ptr(&ptr->ptr_owner, (void *)new);
    507      1.27        ad 
    508      1.27        ad 			/* Wake the writer. */
    509      1.27        ad 			thread->pt_rwlocked = _RW_LOCKED;
    510  1.29.2.1  wrstuden 			pthread__unpark(&ptr->ptr_wblocked, self,
    511  1.29.2.1  wrstuden 			    interlock);
    512      1.27        ad 		} else {
    513      1.27        ad 			new = 0;
    514      1.27        ad 			PTQ_FOREACH(thread, &ptr->ptr_rblocked, pt_sleep) {
    515      1.27        ad 				/*
    516      1.27        ad 				 * May have already been handed the lock,
    517      1.27        ad 				 * since pthread__unpark_all() can release
    518      1.27        ad 				 * our interlock before awakening all
    519      1.27        ad 				 * threads.
    520      1.27        ad 				 */
    521      1.27        ad 				if (thread->pt_sleepobj == NULL)
    522      1.27        ad 					continue;
    523      1.27        ad 				new += RW_READ_INCR;
    524      1.27        ad 				thread->pt_rwlocked = _RW_LOCKED;
    525      1.27        ad 			}
    526      1.27        ad 
    527      1.27        ad 			/*
    528      1.27        ad 			 * Set in the new value.  The lock becomes owned
    529      1.27        ad 			 * by the readers that we are about to wake.
    530      1.27        ad 			 */
    531      1.27        ad 			(void)atomic_swap_ptr(&ptr->ptr_owner, (void *)new);
    532      1.27        ad 
    533      1.27        ad 			/* Wake up all sleeping readers. */
    534      1.27        ad 			ptr->ptr_nreaders = 0;
    535  1.29.2.1  wrstuden 			pthread__unpark_all(&ptr->ptr_rblocked, self,
    536  1.29.2.1  wrstuden 			    interlock);
    537       1.2   thorpej 		}
    538  1.29.2.1  wrstuden 		pthread_mutex_unlock(interlock);
    539      1.27        ad 
    540      1.27        ad 		return 0;
    541       1.2   thorpej 	}
    542      1.27        ad }
    543      1.27        ad 
    544      1.27        ad /*
    545      1.27        ad  * Called when a timedlock awakens early to adjust the waiter bits.
    546      1.27        ad  * The rwlock's interlock is held on entry, and the caller has been
    547      1.27        ad  * removed from the waiters lists.
    548      1.27        ad  */
    549      1.27        ad static void
    550      1.27        ad pthread__rwlock_early(void *obj)
    551      1.27        ad {
    552      1.27        ad 	uintptr_t owner, set, new, next;
    553      1.27        ad 	pthread_rwlock_t *ptr;
    554      1.27        ad 	pthread_t self;
    555      1.27        ad 	u_int off;
    556       1.2   thorpej 
    557      1.27        ad 	self = pthread__self();
    558      1.27        ad 
    559      1.27        ad 	switch (self->pt_rwlocked) {
    560      1.27        ad 	case _RW_WANT_READ:
    561      1.27        ad 		off = offsetof(pthread_rwlock_t, ptr_rblocked);
    562      1.27        ad 		break;
    563      1.27        ad 	case _RW_WANT_WRITE:
    564      1.27        ad 		off = offsetof(pthread_rwlock_t, ptr_wblocked);
    565      1.27        ad 		break;
    566      1.27        ad 	default:
    567      1.27        ad 		pthread__errorfunc(__FILE__, __LINE__, __func__,
    568      1.27        ad 		    "bad value of pt_rwlocked");
    569      1.27        ad 		off = 0;
    570      1.27        ad 		/* NOTREACHED */
    571      1.27        ad 		break;
    572      1.27        ad 	}
    573      1.27        ad 
    574      1.27        ad 	/* LINTED mind your own business */
    575      1.27        ad 	ptr = (pthread_rwlock_t *)((uint8_t *)obj - off);
    576      1.27        ad 	owner = (uintptr_t)ptr->ptr_owner;
    577      1.27        ad 
    578      1.27        ad 	if ((owner & RW_THREAD) == 0) {
    579      1.27        ad 		pthread__errorfunc(__FILE__, __LINE__, __func__,
    580      1.27        ad 		    "lock not held");
    581      1.27        ad 	}
    582      1.27        ad 
    583      1.27        ad 	if (!PTQ_EMPTY(&ptr->ptr_wblocked))
    584      1.27        ad 		set = RW_HAS_WAITERS | RW_WRITE_WANTED;
    585      1.27        ad 	else if (ptr->ptr_nreaders != 0)
    586      1.27        ad 		set = RW_HAS_WAITERS;
    587      1.14        ad 	else
    588      1.27        ad 		set = 0;
    589       1.6        cl 
    590      1.27        ad 	for (;; owner = next) {
    591      1.27        ad 		new = (owner & ~(RW_HAS_WAITERS | RW_WRITE_WANTED)) | set;
    592      1.27        ad 		next = rw_cas(ptr, owner, new);
    593      1.27        ad 		if (owner == next)
    594      1.27        ad 			break;
    595      1.27        ad 	}
    596       1.2   thorpej }
    597       1.2   thorpej 
    598       1.2   thorpej int
    599      1.27        ad _pthread_rwlock_held_np(pthread_rwlock_t *ptr)
    600       1.2   thorpej {
    601      1.27        ad 	uintptr_t owner = (uintptr_t)ptr->ptr_owner;
    602       1.2   thorpej 
    603      1.28        ad 	if ((owner & RW_WRITE_LOCKED) != 0)
    604      1.28        ad 		return (owner & RW_THREAD) == (uintptr_t)pthread__self();
    605      1.27        ad 	return (owner & RW_THREAD) != 0;
    606       1.2   thorpej }
    607       1.2   thorpej 
    608       1.2   thorpej int
    609      1.27        ad _pthread_rwlock_rdheld_np(pthread_rwlock_t *ptr)
    610       1.2   thorpej {
    611      1.27        ad 	uintptr_t owner = (uintptr_t)ptr->ptr_owner;
    612       1.2   thorpej 
    613      1.27        ad 	return (owner & RW_THREAD) != 0 && (owner & RW_WRITE_LOCKED) == 0;
    614       1.2   thorpej }
    615      1.21        ad 
    616      1.23        ad int
    617      1.27        ad _pthread_rwlock_wrheld_np(pthread_rwlock_t *ptr)
    618      1.23        ad {
    619      1.27        ad 	uintptr_t owner = (uintptr_t)ptr->ptr_owner;
    620      1.23        ad 
    621      1.27        ad 	return (owner & (RW_THREAD | RW_WRITE_LOCKED)) ==
    622      1.27        ad 	    ((uintptr_t)pthread__self() | RW_WRITE_LOCKED);
    623      1.23        ad }
    624      1.23        ad 
    625      1.23        ad int
    626      1.27        ad pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
    627      1.23        ad {
    628      1.23        ad 
    629      1.27        ad 	if (attr == NULL)
    630      1.27        ad 		return EINVAL;
    631      1.27        ad 	attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
    632      1.27        ad 
    633      1.27        ad 	return 0;
    634      1.23        ad }
    635      1.23        ad 
    636      1.27        ad 
    637      1.23        ad int
    638      1.27        ad pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
    639      1.23        ad {
    640      1.23        ad 
    641      1.27        ad 	if ((attr == NULL) ||
    642      1.27        ad 	    (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
    643      1.27        ad 		return EINVAL;
    644      1.27        ad 	attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
    645      1.27        ad 
    646      1.27        ad 	return 0;
    647      1.23        ad }
    648