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