2 * Copyright (c) 1998 Alex Nash
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * $FreeBSD: src/lib/libpthread/thread/thr_rwlock.c,v 1.14 2004/01/08 15:37:09 deischen Exp $
27 * $DragonFly: src/lib/libthread_xu/thread/thr_rwlock.c,v 1.6 2006/04/05 12:12:23 davidxu Exp $
30 #include <machine/tls.h>
37 #include "thr_private.h"
39 /* maximum number of times a read lock may be obtained */
40 #define MAX_READ_LOCKS (INT_MAX - 1)
42 umtx_t _rwlock_static_lock;
45 rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
47 pthread_rwlock_t prwlock;
50 /* allocate rwlock object */
51 prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
56 /* initialize the lock */
57 if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0)
60 /* initialize the read condition signal */
61 ret = _pthread_cond_init(&prwlock->read_signal, NULL);
64 _pthread_mutex_destroy(&prwlock->lock);
67 /* initialize the write condition signal */
68 ret = _pthread_cond_init(&prwlock->write_signal, NULL);
71 _pthread_cond_destroy(&prwlock->read_signal);
72 _pthread_mutex_destroy(&prwlock->lock);
77 prwlock->blocked_writers = 0;
87 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
94 pthread_rwlock_t prwlock;
98 _pthread_mutex_destroy(&prwlock->lock);
99 _pthread_cond_destroy(&prwlock->read_signal);
100 _pthread_cond_destroy(&prwlock->write_signal);
111 init_static(struct pthread *thread, pthread_rwlock_t *rwlock)
115 THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
118 ret = rwlock_init(rwlock, NULL);
122 THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
128 _pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
131 return (rwlock_init(rwlock, attr));
135 rwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
137 struct pthread *curthread = tls_get_curthread();
138 pthread_rwlock_t prwlock;
146 /* check for static initialization */
147 if (prwlock == NULL) {
148 if ((ret = init_static(curthread, rwlock)) != 0)
154 /* grab the monitor lock */
155 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
158 /* check lock count */
159 if (prwlock->state == MAX_READ_LOCKS) {
160 _pthread_mutex_unlock(&prwlock->lock);
164 curthread = tls_get_curthread();
165 if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
167 * To avoid having to track all the rdlocks held by
168 * a thread or all of the threads that hold a rdlock,
169 * we keep a simple count of all the rdlocks held by
170 * a thread. If a thread holds any rdlocks it is
171 * possible that it is attempting to take a recursive
172 * rdlock. If there are blocked writers and precedence
173 * is given to them, then that would result in the thread
174 * deadlocking. So allowing a thread to take the rdlock
175 * when it already has one or more rdlocks avoids the
176 * deadlock. I hope the reader can follow that logic ;-)
178 ; /* nothing needed */
180 /* give writers priority over readers */
181 while (prwlock->blocked_writers || prwlock->state < 0) {
183 ret = _pthread_cond_timedwait
184 (&prwlock->read_signal,
185 &prwlock->lock, abstime);
187 ret = _pthread_cond_wait(&prwlock->read_signal,
190 /* can't do a whole lot if this fails */
191 _pthread_mutex_unlock(&prwlock->lock);
197 curthread->rdlock_count++;
198 prwlock->state++; /* indicate we are locked for reading */
201 * Something is really wrong if this call fails. Returning
202 * error won't do because we've already obtained the read
203 * lock. Decrementing 'state' is no good because we probably
204 * don't have the monitor lock.
206 _pthread_mutex_unlock(&prwlock->lock);
212 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
214 return (rwlock_rdlock_common(rwlock, NULL));
218 _pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
219 const struct timespec *abstime)
221 return (rwlock_rdlock_common(rwlock, abstime));
225 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
227 struct pthread *curthread = tls_get_curthread();
228 pthread_rwlock_t prwlock;
236 /* check for static initialization */
237 if (prwlock == NULL) {
238 if ((ret = init_static(curthread, rwlock)) != 0)
244 /* grab the monitor lock */
245 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
248 curthread = tls_get_curthread();
249 if (prwlock->state == MAX_READ_LOCKS)
251 else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
252 /* see comment for pthread_rwlock_rdlock() */
253 curthread->rdlock_count++;
256 /* give writers priority over readers */
257 else if (prwlock->blocked_writers || prwlock->state < 0)
260 curthread->rdlock_count++;
261 prwlock->state++; /* indicate we are locked for reading */
264 /* see the comment on this in pthread_rwlock_rdlock */
265 _pthread_mutex_unlock(&prwlock->lock);
271 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
273 struct pthread *curthread = tls_get_curthread();
274 pthread_rwlock_t prwlock;
282 /* check for static initialization */
283 if (prwlock == NULL) {
284 if ((ret = init_static(curthread, rwlock)) != 0)
290 /* grab the monitor lock */
291 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
294 if (prwlock->state != 0)
297 /* indicate we are locked for writing */
300 /* see the comment on this in pthread_rwlock_rdlock */
301 _pthread_mutex_unlock(&prwlock->lock);
307 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
309 struct pthread *curthread;
310 pthread_rwlock_t prwlock;
321 /* grab the monitor lock */
322 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
325 curthread = tls_get_curthread();
326 if (prwlock->state > 0) {
327 curthread->rdlock_count--;
329 if (prwlock->state == 0 && prwlock->blocked_writers)
330 ret = _pthread_cond_signal(&prwlock->write_signal);
331 } else if (prwlock->state < 0) {
334 if (prwlock->blocked_writers)
335 ret = _pthread_cond_signal(&prwlock->write_signal);
337 ret = _pthread_cond_broadcast(&prwlock->read_signal);
341 /* see the comment on this in pthread_rwlock_rdlock */
342 _pthread_mutex_unlock(&prwlock->lock);
348 rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
350 struct pthread *curthread = tls_get_curthread();
351 pthread_rwlock_t prwlock;
359 /* check for static initialization */
360 if (prwlock == NULL) {
361 if ((ret = init_static(curthread, rwlock)) != 0)
367 /* grab the monitor lock */
368 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
371 while (prwlock->state != 0) {
372 prwlock->blocked_writers++;
375 ret = _pthread_cond_timedwait(&prwlock->write_signal,
376 &prwlock->lock, abstime);
378 ret = _pthread_cond_wait(&prwlock->write_signal,
381 prwlock->blocked_writers--;
382 _pthread_mutex_unlock(&prwlock->lock);
386 prwlock->blocked_writers--;
389 /* indicate we are locked for writing */
392 /* see the comment on this in pthread_rwlock_rdlock */
393 _pthread_mutex_unlock(&prwlock->lock);
399 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
401 return (rwlock_wrlock_common (rwlock, NULL));
405 _pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
406 const struct timespec *abstime)
408 return (rwlock_wrlock_common (rwlock, abstime));
411 __strong_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
412 __strong_reference(_pthread_rwlock_init, pthread_rwlock_init);
413 __strong_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
414 __strong_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
415 __strong_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
416 __strong_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
417 __strong_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
418 __strong_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
419 __strong_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);