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