xref: /dflybsd-src/lib/libthread_xu/thread/thr_rwlock.c (revision cf8046a92768d53e67d2533fb51b137d5506248d)
171b3fa15SDavid Xu /*-
271b3fa15SDavid Xu  * Copyright (c) 1998 Alex Nash
371b3fa15SDavid Xu  * All rights reserved.
471b3fa15SDavid Xu  *
571b3fa15SDavid Xu  * Redistribution and use in source and binary forms, with or without
671b3fa15SDavid Xu  * modification, are permitted provided that the following conditions
771b3fa15SDavid Xu  * are met:
871b3fa15SDavid Xu  * 1. Redistributions of source code must retain the above copyright
971b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer.
1071b3fa15SDavid Xu  * 2. Redistributions in binary form must reproduce the above copyright
1171b3fa15SDavid Xu  *    notice, this list of conditions and the following disclaimer in the
1271b3fa15SDavid Xu  *    documentation and/or other materials provided with the distribution.
1371b3fa15SDavid Xu  *
1471b3fa15SDavid Xu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1571b3fa15SDavid Xu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1671b3fa15SDavid Xu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1771b3fa15SDavid Xu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1871b3fa15SDavid Xu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1971b3fa15SDavid Xu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2071b3fa15SDavid Xu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2171b3fa15SDavid Xu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2271b3fa15SDavid Xu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2371b3fa15SDavid Xu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2471b3fa15SDavid Xu  * SUCH DAMAGE.
2571b3fa15SDavid Xu  *
2671b3fa15SDavid Xu  * $FreeBSD: src/lib/libpthread/thread/thr_rwlock.c,v 1.14 2004/01/08 15:37:09 deischen Exp $
2771b3fa15SDavid Xu  */
2871b3fa15SDavid Xu 
29fc71f871SDavid Xu #include "namespace.h"
309e2ee207SJoerg Sonnenberger #include <machine/tls.h>
3171b3fa15SDavid Xu #include <errno.h>
3271b3fa15SDavid Xu #include <limits.h>
3371b3fa15SDavid Xu #include <stdlib.h>
3471b3fa15SDavid Xu #include <pthread.h>
35fc71f871SDavid Xu #include "un-namespace.h"
3671b3fa15SDavid Xu #include "thr_private.h"
3771b3fa15SDavid Xu 
38fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
39fcaa7a3aSMatthew Dillon 
40fcaa7a3aSMatthew Dillon #include <stdio.h>
41fcaa7a3aSMatthew Dillon #include <stdarg.h>
42fcaa7a3aSMatthew Dillon #include <string.h>
43fcaa7a3aSMatthew Dillon #include <sys/file.h>
44fcaa7a3aSMatthew Dillon 
45fcaa7a3aSMatthew Dillon #endif
46fcaa7a3aSMatthew Dillon 
4771b3fa15SDavid Xu /* maximum number of times a read lock may be obtained */
4871b3fa15SDavid Xu #define	MAX_READ_LOCKS		(INT_MAX - 1)
4971b3fa15SDavid Xu 
50e8382b15SDavid Xu umtx_t	_rwlock_static_lock;
5171b3fa15SDavid Xu 
52fcaa7a3aSMatthew Dillon #ifdef _PTHREADS_DEBUGGING
53fcaa7a3aSMatthew Dillon 
54fcaa7a3aSMatthew Dillon static
55fcaa7a3aSMatthew Dillon void
rwlock_log(const char * ctl,...)56fcaa7a3aSMatthew Dillon rwlock_log(const char *ctl, ...)
57fcaa7a3aSMatthew Dillon {
58fcaa7a3aSMatthew Dillon 	char buf[256];
59fcaa7a3aSMatthew Dillon 	va_list va;
60fcaa7a3aSMatthew Dillon 	size_t len;
61fcaa7a3aSMatthew Dillon 
62fcaa7a3aSMatthew Dillon 	va_start(va, ctl);
63fcaa7a3aSMatthew Dillon 	len = vsnprintf(buf, sizeof(buf), ctl, va);
64fcaa7a3aSMatthew Dillon 	va_end(va);
65fcaa7a3aSMatthew Dillon 	_thr_log(buf, len);
66fcaa7a3aSMatthew Dillon }
67fcaa7a3aSMatthew Dillon 
68fcaa7a3aSMatthew Dillon #else
69fcaa7a3aSMatthew Dillon 
70fcaa7a3aSMatthew Dillon static __inline
71fcaa7a3aSMatthew Dillon void
rwlock_log(const char * ctl __unused,...)72fcaa7a3aSMatthew Dillon rwlock_log(const char *ctl __unused, ...)
73fcaa7a3aSMatthew Dillon {
74fcaa7a3aSMatthew Dillon }
75fcaa7a3aSMatthew Dillon 
76fcaa7a3aSMatthew Dillon #endif
77fcaa7a3aSMatthew Dillon 
7871b3fa15SDavid Xu static int
rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr __unused)79fc71f871SDavid Xu rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr __unused)
8071b3fa15SDavid Xu {
8171b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
8271b3fa15SDavid Xu 	int ret;
8371b3fa15SDavid Xu 
8471b3fa15SDavid Xu 	/* allocate rwlock object */
85*cf8046a9Szrj 	prwlock = __malloc(sizeof(struct __pthread_rwlock_s));
8671b3fa15SDavid Xu 	if (prwlock == NULL)
8771b3fa15SDavid Xu 		return (ENOMEM);
8871b3fa15SDavid Xu 
8971b3fa15SDavid Xu 	/* initialize the lock */
90fcaa7a3aSMatthew Dillon 	if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0) {
91e7bf3f77SMatthew Dillon 		__free(prwlock);
92fcaa7a3aSMatthew Dillon 	} else {
9371b3fa15SDavid Xu 		/* initialize the read condition signal */
9471b3fa15SDavid Xu 		ret = _pthread_cond_init(&prwlock->read_signal, NULL);
9571b3fa15SDavid Xu 
9671b3fa15SDavid Xu 		if (ret != 0) {
9771b3fa15SDavid Xu 			_pthread_mutex_destroy(&prwlock->lock);
98e7bf3f77SMatthew Dillon 			__free(prwlock);
9971b3fa15SDavid Xu 		} else {
10071b3fa15SDavid Xu 			/* initialize the write condition signal */
10171b3fa15SDavid Xu 			ret = _pthread_cond_init(&prwlock->write_signal, NULL);
10271b3fa15SDavid Xu 
10371b3fa15SDavid Xu 			if (ret != 0) {
10471b3fa15SDavid Xu 				_pthread_cond_destroy(&prwlock->read_signal);
10571b3fa15SDavid Xu 				_pthread_mutex_destroy(&prwlock->lock);
106e7bf3f77SMatthew Dillon 				__free(prwlock);
10771b3fa15SDavid Xu 			} else {
10871b3fa15SDavid Xu 				/* success */
10971b3fa15SDavid Xu 				prwlock->state = 0;
11071b3fa15SDavid Xu 				prwlock->blocked_writers = 0;
11171b3fa15SDavid Xu 				*rwlock = prwlock;
11271b3fa15SDavid Xu 			}
11371b3fa15SDavid Xu 		}
11471b3fa15SDavid Xu 	}
11571b3fa15SDavid Xu 
11671b3fa15SDavid Xu 	return (ret);
11771b3fa15SDavid Xu }
11871b3fa15SDavid Xu 
119fcaa7a3aSMatthew Dillon #if 0
120fcaa7a3aSMatthew Dillon void
121fcaa7a3aSMatthew Dillon _rwlock_reinit(pthread_rwlock_t prwlock)
122fcaa7a3aSMatthew Dillon {
123fcaa7a3aSMatthew Dillon 	_mutex_reinit(&prwlock->lock);
124fcaa7a3aSMatthew Dillon 	_cond_reinit(prwlock->read_signal);
125fcaa7a3aSMatthew Dillon 	prwlock->state = 0;
126fcaa7a3aSMatthew Dillon 	prwlock->blocked_writers = 0;
127fcaa7a3aSMatthew Dillon }
128fcaa7a3aSMatthew Dillon #endif
129fcaa7a3aSMatthew Dillon 
13071b3fa15SDavid Xu int
_pthread_rwlock_destroy(pthread_rwlock_t * rwlock)13171b3fa15SDavid Xu _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
13271b3fa15SDavid Xu {
13371b3fa15SDavid Xu 	int ret;
13471b3fa15SDavid Xu 
135fcaa7a3aSMatthew Dillon 	if (rwlock == NULL) {
13671b3fa15SDavid Xu 		ret = EINVAL;
137fcaa7a3aSMatthew Dillon 	} else if (*rwlock == NULL) {
138146da5fcSMichael Neumann 		ret = 0;
139fcaa7a3aSMatthew Dillon 	} else {
14071b3fa15SDavid Xu 		pthread_rwlock_t prwlock;
14171b3fa15SDavid Xu 
14271b3fa15SDavid Xu 		prwlock = *rwlock;
143fcaa7a3aSMatthew Dillon 		rwlock_log("rwlock_destroy %p\n", prwlock);
14471b3fa15SDavid Xu 
14571b3fa15SDavid Xu 		_pthread_mutex_destroy(&prwlock->lock);
14671b3fa15SDavid Xu 		_pthread_cond_destroy(&prwlock->read_signal);
14771b3fa15SDavid Xu 		_pthread_cond_destroy(&prwlock->write_signal);
148e7bf3f77SMatthew Dillon 		__free(prwlock);
14971b3fa15SDavid Xu 
15071b3fa15SDavid Xu 		*rwlock = NULL;
15171b3fa15SDavid Xu 
15271b3fa15SDavid Xu 		ret = 0;
15371b3fa15SDavid Xu 	}
15471b3fa15SDavid Xu 	return (ret);
15571b3fa15SDavid Xu }
15671b3fa15SDavid Xu 
15771b3fa15SDavid Xu static int
init_static(pthread_t thread,pthread_rwlock_t * rwlock)158940be950Szrj init_static(pthread_t thread, pthread_rwlock_t *rwlock)
15971b3fa15SDavid Xu {
16071b3fa15SDavid Xu 	int ret;
16171b3fa15SDavid Xu 
16271b3fa15SDavid Xu 	THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
16371b3fa15SDavid Xu 
16471b3fa15SDavid Xu 	if (*rwlock == NULL)
16571b3fa15SDavid Xu 		ret = rwlock_init(rwlock, NULL);
16671b3fa15SDavid Xu 	else
16771b3fa15SDavid Xu 		ret = 0;
16871b3fa15SDavid Xu 
16971b3fa15SDavid Xu 	THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
17071b3fa15SDavid Xu 
17171b3fa15SDavid Xu 	return (ret);
17271b3fa15SDavid Xu }
17371b3fa15SDavid Xu 
17471b3fa15SDavid Xu int
_pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr)17571b3fa15SDavid Xu _pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
17671b3fa15SDavid Xu {
17771b3fa15SDavid Xu 	*rwlock = NULL;
17871b3fa15SDavid Xu 	return (rwlock_init(rwlock, attr));
17971b3fa15SDavid Xu }
18071b3fa15SDavid Xu 
18171b3fa15SDavid Xu static int
rwlock_rdlock_common(pthread_rwlock_t * rwlock,const struct timespec * abstime)18271b3fa15SDavid Xu rwlock_rdlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime)
18371b3fa15SDavid Xu {
184940be950Szrj 	pthread_t curthread = tls_get_curthread();
18571b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
18671b3fa15SDavid Xu 	int ret;
18771b3fa15SDavid Xu 
18871b3fa15SDavid Xu 	if (rwlock == NULL)
18971b3fa15SDavid Xu 		return (EINVAL);
19071b3fa15SDavid Xu 
19171b3fa15SDavid Xu 	prwlock = *rwlock;
19271b3fa15SDavid Xu 
19371b3fa15SDavid Xu 	/* check for static initialization */
19471b3fa15SDavid Xu 	if (prwlock == NULL) {
19571b3fa15SDavid Xu 		if ((ret = init_static(curthread, rwlock)) != 0)
19671b3fa15SDavid Xu 			return (ret);
19771b3fa15SDavid Xu 
19871b3fa15SDavid Xu 		prwlock = *rwlock;
19971b3fa15SDavid Xu 	}
200fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_rdlock_common %p\n", prwlock);
20171b3fa15SDavid Xu 
20271b3fa15SDavid Xu 	/* grab the monitor lock */
203fcaa7a3aSMatthew Dillon 	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) {
204fcaa7a3aSMatthew Dillon 		rwlock_log("rwlock_rdlock_common %p (failedA)\n", prwlock);
20571b3fa15SDavid Xu 		return (ret);
206fcaa7a3aSMatthew Dillon 	}
20771b3fa15SDavid Xu 
20871b3fa15SDavid Xu 	/* check lock count */
20971b3fa15SDavid Xu 	if (prwlock->state == MAX_READ_LOCKS) {
2109676deceSDavid Xu 		_pthread_mutex_unlock(&prwlock->lock);
211fcaa7a3aSMatthew Dillon 		rwlock_log("rwlock_rdlock_common %p (failedB)\n", prwlock);
21271b3fa15SDavid Xu 		return (EAGAIN);
21371b3fa15SDavid Xu 	}
21471b3fa15SDavid Xu 
2159e2ee207SJoerg Sonnenberger 	curthread = tls_get_curthread();
21671b3fa15SDavid Xu 	if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
21771b3fa15SDavid Xu 		/*
21871b3fa15SDavid Xu 		 * To avoid having to track all the rdlocks held by
21971b3fa15SDavid Xu 		 * a thread or all of the threads that hold a rdlock,
22071b3fa15SDavid Xu 		 * we keep a simple count of all the rdlocks held by
22171b3fa15SDavid Xu 		 * a thread.  If a thread holds any rdlocks it is
22271b3fa15SDavid Xu 		 * possible that it is attempting to take a recursive
22371b3fa15SDavid Xu 		 * rdlock.  If there are blocked writers and precedence
22471b3fa15SDavid Xu 		 * is given to them, then that would result in the thread
22571b3fa15SDavid Xu 		 * deadlocking.  So allowing a thread to take the rdlock
22671b3fa15SDavid Xu 		 * when it already has one or more rdlocks avoids the
22771b3fa15SDavid Xu 		 * deadlock.  I hope the reader can follow that logic ;-)
22871b3fa15SDavid Xu 		 */
22971b3fa15SDavid Xu 		;	/* nothing needed */
23071b3fa15SDavid Xu 	} else {
231fcaa7a3aSMatthew Dillon 		/*
232fcaa7a3aSMatthew Dillon 		 * Give writers priority over readers
233fcaa7a3aSMatthew Dillon 		 *
234fcaa7a3aSMatthew Dillon 		 * WARNING: pthread_cond*() temporarily releases the
235fcaa7a3aSMatthew Dillon 		 *	    mutex.
236fcaa7a3aSMatthew Dillon 		 */
23771b3fa15SDavid Xu 		while (prwlock->blocked_writers || prwlock->state < 0) {
238fcaa7a3aSMatthew Dillon 			if (abstime) {
239fcaa7a3aSMatthew Dillon 				ret = _pthread_cond_timedwait(
240fcaa7a3aSMatthew Dillon 					    &prwlock->read_signal,
24171b3fa15SDavid Xu 					    &prwlock->lock, abstime);
242fcaa7a3aSMatthew Dillon 			} else {
243fcaa7a3aSMatthew Dillon 				ret = _pthread_cond_wait(
244fcaa7a3aSMatthew Dillon 					    &prwlock->read_signal,
24571b3fa15SDavid Xu 					    &prwlock->lock);
246fcaa7a3aSMatthew Dillon 			}
24771b3fa15SDavid Xu 			if (ret != 0) {
24871b3fa15SDavid Xu 				/* can't do a whole lot if this fails */
2499676deceSDavid Xu 				_pthread_mutex_unlock(&prwlock->lock);
250fcaa7a3aSMatthew Dillon 				rwlock_log("rwlock_rdlock_common %p "
251fcaa7a3aSMatthew Dillon 					   "(failedC)\n", prwlock);
25271b3fa15SDavid Xu 				return (ret);
25371b3fa15SDavid Xu 			}
25471b3fa15SDavid Xu 		}
25571b3fa15SDavid Xu 	}
25671b3fa15SDavid Xu 
25771b3fa15SDavid Xu 	curthread->rdlock_count++;
25871b3fa15SDavid Xu 	prwlock->state++; /* indicate we are locked for reading */
25971b3fa15SDavid Xu 
26071b3fa15SDavid Xu 	/*
26171b3fa15SDavid Xu 	 * Something is really wrong if this call fails.  Returning
26271b3fa15SDavid Xu 	 * error won't do because we've already obtained the read
26371b3fa15SDavid Xu 	 * lock.  Decrementing 'state' is no good because we probably
26471b3fa15SDavid Xu 	 * don't have the monitor lock.
26571b3fa15SDavid Xu 	 */
2669676deceSDavid Xu 	_pthread_mutex_unlock(&prwlock->lock);
267fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_rdlock_common %p (return %d)\n", prwlock, ret);
26871b3fa15SDavid Xu 
26971b3fa15SDavid Xu 	return (ret);
27071b3fa15SDavid Xu }
27171b3fa15SDavid Xu 
27271b3fa15SDavid Xu int
_pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)27371b3fa15SDavid Xu _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
27471b3fa15SDavid Xu {
27571b3fa15SDavid Xu 	return (rwlock_rdlock_common(rwlock, NULL));
27671b3fa15SDavid Xu }
27771b3fa15SDavid Xu 
27871b3fa15SDavid Xu int
_pthread_rwlock_timedrdlock(pthread_rwlock_t * __restrict rwlock,const struct timespec * __restrict abstime)279d33005aaSSascha Wildner _pthread_rwlock_timedrdlock (pthread_rwlock_t * __restrict rwlock,
280d33005aaSSascha Wildner     const struct timespec * __restrict abstime)
28171b3fa15SDavid Xu {
28271b3fa15SDavid Xu 	return (rwlock_rdlock_common(rwlock, abstime));
28371b3fa15SDavid Xu }
28471b3fa15SDavid Xu 
28571b3fa15SDavid Xu int
_pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)28671b3fa15SDavid Xu _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
28771b3fa15SDavid Xu {
288940be950Szrj 	pthread_t curthread = tls_get_curthread();
28971b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
29071b3fa15SDavid Xu 	int ret;
29171b3fa15SDavid Xu 
29271b3fa15SDavid Xu 	if (rwlock == NULL)
29371b3fa15SDavid Xu 		return (EINVAL);
29471b3fa15SDavid Xu 
29571b3fa15SDavid Xu 	prwlock = *rwlock;
29671b3fa15SDavid Xu 
29771b3fa15SDavid Xu 	/* check for static initialization */
29871b3fa15SDavid Xu 	if (prwlock == NULL) {
29971b3fa15SDavid Xu 		if ((ret = init_static(curthread, rwlock)) != 0)
30071b3fa15SDavid Xu 			return (ret);
30171b3fa15SDavid Xu 
30271b3fa15SDavid Xu 		prwlock = *rwlock;
30371b3fa15SDavid Xu 	}
30471b3fa15SDavid Xu 
30571b3fa15SDavid Xu 	/* grab the monitor lock */
30671b3fa15SDavid Xu 	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
30771b3fa15SDavid Xu 		return (ret);
30871b3fa15SDavid Xu 
3099e2ee207SJoerg Sonnenberger 	curthread = tls_get_curthread();
31071b3fa15SDavid Xu 	if (prwlock->state == MAX_READ_LOCKS)
31171b3fa15SDavid Xu 		ret = EAGAIN;
31271b3fa15SDavid Xu 	else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
31371b3fa15SDavid Xu 		/* see comment for pthread_rwlock_rdlock() */
31471b3fa15SDavid Xu 		curthread->rdlock_count++;
31571b3fa15SDavid Xu 		prwlock->state++;
31671b3fa15SDavid Xu 	}
31771b3fa15SDavid Xu 	/* give writers priority over readers */
31871b3fa15SDavid Xu 	else if (prwlock->blocked_writers || prwlock->state < 0)
31971b3fa15SDavid Xu 		ret = EBUSY;
32071b3fa15SDavid Xu 	else {
32171b3fa15SDavid Xu 		curthread->rdlock_count++;
32271b3fa15SDavid Xu 		prwlock->state++; /* indicate we are locked for reading */
32371b3fa15SDavid Xu 	}
32471b3fa15SDavid Xu 
32571b3fa15SDavid Xu 	/* see the comment on this in pthread_rwlock_rdlock */
32671b3fa15SDavid Xu 	_pthread_mutex_unlock(&prwlock->lock);
32771b3fa15SDavid Xu 
32871b3fa15SDavid Xu 	return (ret);
32971b3fa15SDavid Xu }
33071b3fa15SDavid Xu 
33171b3fa15SDavid Xu int
_pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)33271b3fa15SDavid Xu _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
33371b3fa15SDavid Xu {
334940be950Szrj 	pthread_t curthread = tls_get_curthread();
33571b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
33671b3fa15SDavid Xu 	int ret;
33771b3fa15SDavid Xu 
33871b3fa15SDavid Xu 	if (rwlock == NULL)
33971b3fa15SDavid Xu 		return (EINVAL);
34071b3fa15SDavid Xu 
34171b3fa15SDavid Xu 	prwlock = *rwlock;
34271b3fa15SDavid Xu 
34371b3fa15SDavid Xu 	/* check for static initialization */
34471b3fa15SDavid Xu 	if (prwlock == NULL) {
34571b3fa15SDavid Xu 		if ((ret = init_static(curthread, rwlock)) != 0)
34671b3fa15SDavid Xu 			return (ret);
34771b3fa15SDavid Xu 
34871b3fa15SDavid Xu 		prwlock = *rwlock;
34971b3fa15SDavid Xu 	}
35071b3fa15SDavid Xu 
35171b3fa15SDavid Xu 	/* grab the monitor lock */
35271b3fa15SDavid Xu 	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
35371b3fa15SDavid Xu 		return (ret);
35471b3fa15SDavid Xu 
35571b3fa15SDavid Xu 	if (prwlock->state != 0)
35671b3fa15SDavid Xu 		ret = EBUSY;
35771b3fa15SDavid Xu 	else
35871b3fa15SDavid Xu 		/* indicate we are locked for writing */
35971b3fa15SDavid Xu 		prwlock->state = -1;
36071b3fa15SDavid Xu 
36171b3fa15SDavid Xu 	/* see the comment on this in pthread_rwlock_rdlock */
36271b3fa15SDavid Xu 	_pthread_mutex_unlock(&prwlock->lock);
36371b3fa15SDavid Xu 
36471b3fa15SDavid Xu 	return (ret);
36571b3fa15SDavid Xu }
36671b3fa15SDavid Xu 
36771b3fa15SDavid Xu int
_pthread_rwlock_unlock(pthread_rwlock_t * rwlock)36871b3fa15SDavid Xu _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
36971b3fa15SDavid Xu {
370940be950Szrj 	pthread_t curthread;
37171b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
37271b3fa15SDavid Xu 	int ret;
37371b3fa15SDavid Xu 
37471b3fa15SDavid Xu 	if (rwlock == NULL)
37571b3fa15SDavid Xu 		return (EINVAL);
37671b3fa15SDavid Xu 
37771b3fa15SDavid Xu 	prwlock = *rwlock;
37871b3fa15SDavid Xu 
37971b3fa15SDavid Xu 	if (prwlock == NULL)
38071b3fa15SDavid Xu 		return (EINVAL);
38171b3fa15SDavid Xu 
382fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_unlock %p\n", prwlock);
383fcaa7a3aSMatthew Dillon 
38471b3fa15SDavid Xu 	/* grab the monitor lock */
3859676deceSDavid Xu 	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
38671b3fa15SDavid Xu 		return (ret);
38771b3fa15SDavid Xu 
3889e2ee207SJoerg Sonnenberger 	curthread = tls_get_curthread();
38971b3fa15SDavid Xu 	if (prwlock->state > 0) {
390fcaa7a3aSMatthew Dillon 		/*
391fcaa7a3aSMatthew Dillon 		 * Unlock reader
392fcaa7a3aSMatthew Dillon 		 */
39371b3fa15SDavid Xu 		curthread->rdlock_count--;
39471b3fa15SDavid Xu 		prwlock->state--;
39571b3fa15SDavid Xu 		if (prwlock->state == 0 && prwlock->blocked_writers)
3969676deceSDavid Xu 			ret = _pthread_cond_signal(&prwlock->write_signal);
39771b3fa15SDavid Xu 	} else if (prwlock->state < 0) {
398fcaa7a3aSMatthew Dillon 		/*
399fcaa7a3aSMatthew Dillon 		 * unlock writer
400fcaa7a3aSMatthew Dillon 		 */
40171b3fa15SDavid Xu 		prwlock->state = 0;
40271b3fa15SDavid Xu 
40371b3fa15SDavid Xu 		if (prwlock->blocked_writers)
4049676deceSDavid Xu 			ret = _pthread_cond_signal(&prwlock->write_signal);
40571b3fa15SDavid Xu 		else
4069676deceSDavid Xu 			ret = _pthread_cond_broadcast(&prwlock->read_signal);
407fcaa7a3aSMatthew Dillon 	} else {
40871b3fa15SDavid Xu 		ret = EINVAL;
409fcaa7a3aSMatthew Dillon 	}
41071b3fa15SDavid Xu 
41171b3fa15SDavid Xu 	/* see the comment on this in pthread_rwlock_rdlock */
4129676deceSDavid Xu 	_pthread_mutex_unlock(&prwlock->lock);
413fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_unlock %p (return %d)\n", prwlock, ret);
41471b3fa15SDavid Xu 
41571b3fa15SDavid Xu 	return (ret);
41671b3fa15SDavid Xu }
41771b3fa15SDavid Xu 
41871b3fa15SDavid Xu static int
rwlock_wrlock_common(pthread_rwlock_t * rwlock,const struct timespec * abstime)41971b3fa15SDavid Xu rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
42071b3fa15SDavid Xu {
421940be950Szrj 	pthread_t curthread = tls_get_curthread();
42271b3fa15SDavid Xu 	pthread_rwlock_t prwlock;
42371b3fa15SDavid Xu 	int ret;
42471b3fa15SDavid Xu 
42571b3fa15SDavid Xu 	if (rwlock == NULL)
42671b3fa15SDavid Xu 		return (EINVAL);
42771b3fa15SDavid Xu 
42871b3fa15SDavid Xu 	prwlock = *rwlock;
42971b3fa15SDavid Xu 
43071b3fa15SDavid Xu 	/* check for static initialization */
43171b3fa15SDavid Xu 	if (prwlock == NULL) {
43271b3fa15SDavid Xu 		if ((ret = init_static(curthread, rwlock)) != 0)
43371b3fa15SDavid Xu 			return (ret);
43471b3fa15SDavid Xu 
43571b3fa15SDavid Xu 		prwlock = *rwlock;
43671b3fa15SDavid Xu 	}
437fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_wrlock_common %p\n", prwlock);
43871b3fa15SDavid Xu 
43971b3fa15SDavid Xu 	/* grab the monitor lock */
440fcaa7a3aSMatthew Dillon 	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0) {
441fcaa7a3aSMatthew Dillon 		rwlock_log("rwlock_wrlock_common %p (failedA)\n", prwlock);
44271b3fa15SDavid Xu 		return (ret);
443fcaa7a3aSMatthew Dillon 	}
44471b3fa15SDavid Xu 
44571b3fa15SDavid Xu 	while (prwlock->state != 0) {
44671b3fa15SDavid Xu 		prwlock->blocked_writers++;
44771b3fa15SDavid Xu 
448fcaa7a3aSMatthew Dillon 		/*
449fcaa7a3aSMatthew Dillon 		 * WARNING: pthread_cond*() temporarily releases the
450fcaa7a3aSMatthew Dillon 		 *	    mutex.
451fcaa7a3aSMatthew Dillon 		 */
452fcaa7a3aSMatthew Dillon 		if (abstime != NULL) {
45371b3fa15SDavid Xu 			ret = _pthread_cond_timedwait(&prwlock->write_signal,
454fcaa7a3aSMatthew Dillon 						      &prwlock->lock,
455fcaa7a3aSMatthew Dillon 						      abstime);
456fcaa7a3aSMatthew Dillon 		} else {
4579676deceSDavid Xu 			ret = _pthread_cond_wait(&prwlock->write_signal,
45871b3fa15SDavid Xu 						 &prwlock->lock);
459fcaa7a3aSMatthew Dillon 		}
460fcaa7a3aSMatthew Dillon 
461fcaa7a3aSMatthew Dillon 		/*
462fcaa7a3aSMatthew Dillon 		 * Undo on failure.  When the blocked_writers count drops
463fcaa7a3aSMatthew Dillon 		 * to 0 we may have to wakeup blocked readers.
464fcaa7a3aSMatthew Dillon 		 */
46571b3fa15SDavid Xu 		if (ret != 0) {
46671b3fa15SDavid Xu 			prwlock->blocked_writers--;
467fcaa7a3aSMatthew Dillon 			if (prwlock->blocked_writers == 0 &&
468fcaa7a3aSMatthew Dillon 			    prwlock->state >= 0) {
469fcaa7a3aSMatthew Dillon 				_pthread_cond_broadcast(&prwlock->read_signal);
470fcaa7a3aSMatthew Dillon 			}
4719676deceSDavid Xu 			_pthread_mutex_unlock(&prwlock->lock);
472fcaa7a3aSMatthew Dillon 			rwlock_log("rwlock_wrlock_common %p (failedB %d)\n",
473fcaa7a3aSMatthew Dillon 				   prwlock, ret);
47471b3fa15SDavid Xu 			return (ret);
47571b3fa15SDavid Xu 		}
47671b3fa15SDavid Xu 
47771b3fa15SDavid Xu 		prwlock->blocked_writers--;
47871b3fa15SDavid Xu 	}
47971b3fa15SDavid Xu 
48071b3fa15SDavid Xu 	/* indicate we are locked for writing */
48171b3fa15SDavid Xu 	prwlock->state = -1;
48271b3fa15SDavid Xu 
48371b3fa15SDavid Xu 	/* see the comment on this in pthread_rwlock_rdlock */
4849676deceSDavid Xu 	_pthread_mutex_unlock(&prwlock->lock);
485fcaa7a3aSMatthew Dillon 	rwlock_log("rwlock_wrlock_common %p (returns %d)\n", prwlock, ret);
48671b3fa15SDavid Xu 
48771b3fa15SDavid Xu 	return (ret);
48871b3fa15SDavid Xu }
48971b3fa15SDavid Xu 
49071b3fa15SDavid Xu int
_pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)49171b3fa15SDavid Xu _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
49271b3fa15SDavid Xu {
49371b3fa15SDavid Xu 	return (rwlock_wrlock_common (rwlock, NULL));
49471b3fa15SDavid Xu }
49571b3fa15SDavid Xu 
49671b3fa15SDavid Xu int
_pthread_rwlock_timedwrlock(pthread_rwlock_t * __restrict rwlock,const struct timespec * __restrict abstime)497d33005aaSSascha Wildner _pthread_rwlock_timedwrlock (pthread_rwlock_t * __restrict rwlock,
498d33005aaSSascha Wildner     const struct timespec * __restrict abstime)
49971b3fa15SDavid Xu {
50071b3fa15SDavid Xu 	return (rwlock_wrlock_common (rwlock, abstime));
50171b3fa15SDavid Xu }
5025a1048c8SDavid Xu 
5035a1048c8SDavid Xu __strong_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
5045a1048c8SDavid Xu __strong_reference(_pthread_rwlock_init, pthread_rwlock_init);
5055a1048c8SDavid Xu __strong_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
5065a1048c8SDavid Xu __strong_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
5075a1048c8SDavid Xu __strong_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
5085a1048c8SDavid Xu __strong_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
5095a1048c8SDavid Xu __strong_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
5105a1048c8SDavid Xu __strong_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
5115a1048c8SDavid Xu __strong_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);
512