i386 removal, part 59/x: Revert a i386 specific local change in dma(8).
[dragonfly.git] / lib / libthread_xu / thread / thr_rwlock.c
blobff9c58122109d9b60d8263466e18979f13f4ed19
1 /*-
2 * Copyright (c) 1998 Alex Nash
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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
24 * SUCH DAMAGE.
26 * $FreeBSD: src/lib/libpthread/thread/thr_rwlock.c,v 1.14 2004/01/08 15:37:09 deischen Exp $
29 #include "namespace.h"
30 #include <machine/tls.h>
31 #include <errno.h>
32 #include <limits.h>
33 #include <stdlib.h>
34 #include <pthread.h>
35 #include "un-namespace.h"
36 #include "thr_private.h"
38 /* maximum number of times a read lock may be obtained */
39 #define MAX_READ_LOCKS (INT_MAX - 1)
41 umtx_t _rwlock_static_lock;
43 static int
44 rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr __unused)
46 pthread_rwlock_t prwlock;
47 int ret;
49 /* allocate rwlock object */
50 prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
52 if (prwlock == NULL)
53 return (ENOMEM);
55 /* initialize the lock */
56 if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0)
57 free(prwlock);
58 else {
59 /* initialize the read condition signal */
60 ret = _pthread_cond_init(&prwlock->read_signal, NULL);
62 if (ret != 0) {
63 _pthread_mutex_destroy(&prwlock->lock);
64 free(prwlock);
65 } else {
66 /* initialize the write condition signal */
67 ret = _pthread_cond_init(&prwlock->write_signal, NULL);
69 if (ret != 0) {
70 _pthread_cond_destroy(&prwlock->read_signal);
71 _pthread_mutex_destroy(&prwlock->lock);
72 free(prwlock);
73 } else {
74 /* success */
75 prwlock->state = 0;
76 prwlock->blocked_writers = 0;
77 *rwlock = prwlock;
82 return (ret);
85 int
86 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
88 int ret;
90 if (rwlock == NULL)
91 ret = EINVAL;
92 else if (*rwlock == NULL)
93 ret = 0;
94 else {
95 pthread_rwlock_t prwlock;
97 prwlock = *rwlock;
99 _pthread_mutex_destroy(&prwlock->lock);
100 _pthread_cond_destroy(&prwlock->read_signal);
101 _pthread_cond_destroy(&prwlock->write_signal);
102 free(prwlock);
104 *rwlock = NULL;
106 ret = 0;
108 return (ret);
111 static int
112 init_static(struct pthread *thread, pthread_rwlock_t *rwlock)
114 int ret;
116 THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
118 if (*rwlock == NULL)
119 ret = rwlock_init(rwlock, NULL);
120 else
121 ret = 0;
123 THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
125 return (ret);
129 _pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
131 *rwlock = NULL;
132 return (rwlock_init(rwlock, attr));
135 static int
136 rwlock_rdlock_common(pthread_rwlock_t *rwlock, const struct timespec *abstime)
138 struct pthread *curthread = tls_get_curthread();
139 pthread_rwlock_t prwlock;
140 int ret;
142 if (rwlock == NULL)
143 return (EINVAL);
145 prwlock = *rwlock;
147 /* check for static initialization */
148 if (prwlock == NULL) {
149 if ((ret = init_static(curthread, rwlock)) != 0)
150 return (ret);
152 prwlock = *rwlock;
155 /* grab the monitor lock */
156 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
157 return (ret);
159 /* check lock count */
160 if (prwlock->state == MAX_READ_LOCKS) {
161 _pthread_mutex_unlock(&prwlock->lock);
162 return (EAGAIN);
165 curthread = tls_get_curthread();
166 if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
168 * To avoid having to track all the rdlocks held by
169 * a thread or all of the threads that hold a rdlock,
170 * we keep a simple count of all the rdlocks held by
171 * a thread. If a thread holds any rdlocks it is
172 * possible that it is attempting to take a recursive
173 * rdlock. If there are blocked writers and precedence
174 * is given to them, then that would result in the thread
175 * deadlocking. So allowing a thread to take the rdlock
176 * when it already has one or more rdlocks avoids the
177 * deadlock. I hope the reader can follow that logic ;-)
179 ; /* nothing needed */
180 } else {
181 /* give writers priority over readers */
182 while (prwlock->blocked_writers || prwlock->state < 0) {
183 if (abstime)
184 ret = _pthread_cond_timedwait
185 (&prwlock->read_signal,
186 &prwlock->lock, abstime);
187 else
188 ret = _pthread_cond_wait(&prwlock->read_signal,
189 &prwlock->lock);
190 if (ret != 0) {
191 /* can't do a whole lot if this fails */
192 _pthread_mutex_unlock(&prwlock->lock);
193 return (ret);
198 curthread->rdlock_count++;
199 prwlock->state++; /* indicate we are locked for reading */
202 * Something is really wrong if this call fails. Returning
203 * error won't do because we've already obtained the read
204 * lock. Decrementing 'state' is no good because we probably
205 * don't have the monitor lock.
207 _pthread_mutex_unlock(&prwlock->lock);
209 return (ret);
213 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
215 return (rwlock_rdlock_common(rwlock, NULL));
219 _pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
220 const struct timespec *abstime)
222 return (rwlock_rdlock_common(rwlock, abstime));
226 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
228 struct pthread *curthread = tls_get_curthread();
229 pthread_rwlock_t prwlock;
230 int ret;
232 if (rwlock == NULL)
233 return (EINVAL);
235 prwlock = *rwlock;
237 /* check for static initialization */
238 if (prwlock == NULL) {
239 if ((ret = init_static(curthread, rwlock)) != 0)
240 return (ret);
242 prwlock = *rwlock;
245 /* grab the monitor lock */
246 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
247 return (ret);
249 curthread = tls_get_curthread();
250 if (prwlock->state == MAX_READ_LOCKS)
251 ret = EAGAIN;
252 else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
253 /* see comment for pthread_rwlock_rdlock() */
254 curthread->rdlock_count++;
255 prwlock->state++;
257 /* give writers priority over readers */
258 else if (prwlock->blocked_writers || prwlock->state < 0)
259 ret = EBUSY;
260 else {
261 curthread->rdlock_count++;
262 prwlock->state++; /* indicate we are locked for reading */
265 /* see the comment on this in pthread_rwlock_rdlock */
266 _pthread_mutex_unlock(&prwlock->lock);
268 return (ret);
272 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
274 struct pthread *curthread = tls_get_curthread();
275 pthread_rwlock_t prwlock;
276 int ret;
278 if (rwlock == NULL)
279 return (EINVAL);
281 prwlock = *rwlock;
283 /* check for static initialization */
284 if (prwlock == NULL) {
285 if ((ret = init_static(curthread, rwlock)) != 0)
286 return (ret);
288 prwlock = *rwlock;
291 /* grab the monitor lock */
292 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
293 return (ret);
295 if (prwlock->state != 0)
296 ret = EBUSY;
297 else
298 /* indicate we are locked for writing */
299 prwlock->state = -1;
301 /* see the comment on this in pthread_rwlock_rdlock */
302 _pthread_mutex_unlock(&prwlock->lock);
304 return (ret);
308 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
310 struct pthread *curthread;
311 pthread_rwlock_t prwlock;
312 int ret;
314 if (rwlock == NULL)
315 return (EINVAL);
317 prwlock = *rwlock;
319 if (prwlock == NULL)
320 return (EINVAL);
322 /* grab the monitor lock */
323 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
324 return (ret);
326 curthread = tls_get_curthread();
327 if (prwlock->state > 0) {
328 curthread->rdlock_count--;
329 prwlock->state--;
330 if (prwlock->state == 0 && prwlock->blocked_writers)
331 ret = _pthread_cond_signal(&prwlock->write_signal);
332 } else if (prwlock->state < 0) {
333 prwlock->state = 0;
335 if (prwlock->blocked_writers)
336 ret = _pthread_cond_signal(&prwlock->write_signal);
337 else
338 ret = _pthread_cond_broadcast(&prwlock->read_signal);
339 } else
340 ret = EINVAL;
342 /* see the comment on this in pthread_rwlock_rdlock */
343 _pthread_mutex_unlock(&prwlock->lock);
345 return (ret);
348 static int
349 rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
351 struct pthread *curthread = tls_get_curthread();
352 pthread_rwlock_t prwlock;
353 int ret;
355 if (rwlock == NULL)
356 return (EINVAL);
358 prwlock = *rwlock;
360 /* check for static initialization */
361 if (prwlock == NULL) {
362 if ((ret = init_static(curthread, rwlock)) != 0)
363 return (ret);
365 prwlock = *rwlock;
368 /* grab the monitor lock */
369 if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
370 return (ret);
372 while (prwlock->state != 0) {
373 prwlock->blocked_writers++;
375 if (abstime != NULL)
376 ret = _pthread_cond_timedwait(&prwlock->write_signal,
377 &prwlock->lock, abstime);
378 else
379 ret = _pthread_cond_wait(&prwlock->write_signal,
380 &prwlock->lock);
381 if (ret != 0) {
382 prwlock->blocked_writers--;
383 _pthread_mutex_unlock(&prwlock->lock);
384 return (ret);
387 prwlock->blocked_writers--;
390 /* indicate we are locked for writing */
391 prwlock->state = -1;
393 /* see the comment on this in pthread_rwlock_rdlock */
394 _pthread_mutex_unlock(&prwlock->lock);
396 return (ret);
400 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
402 return (rwlock_wrlock_common (rwlock, NULL));
406 _pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
407 const struct timespec *abstime)
409 return (rwlock_wrlock_common (rwlock, abstime));
412 __strong_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
413 __strong_reference(_pthread_rwlock_init, pthread_rwlock_init);
414 __strong_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
415 __strong_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
416 __strong_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
417 __strong_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
418 __strong_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
419 __strong_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
420 __strong_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);