Move some global variables into its module, remove priority mutex code
[dragonfly.git] / lib / libthread_xu / thread / thr_rwlock.c
1 /*-
2  * Copyright (c) 1998 Alex Nash
3  * All rights reserved.
4  *
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.
13  *
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.
25  *
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 $
28  */
29
30 #include <machine/tls.h>
31
32 #include <errno.h>
33 #include <limits.h>
34 #include <stdlib.h>
35
36 #include <pthread.h>
37 #include "thr_private.h"
38
39 /* maximum number of times a read lock may be obtained */
40 #define MAX_READ_LOCKS          (INT_MAX - 1)
41
42 umtx_t  _rwlock_static_lock;
43
44 static int
45 rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
46 {
47         pthread_rwlock_t prwlock;
48         int ret;
49
50         /* allocate rwlock object */
51         prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
52
53         if (prwlock == NULL)
54                 return (ENOMEM);
55
56         /* initialize the lock */
57         if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0)
58                 free(prwlock);
59         else {
60                 /* initialize the read condition signal */
61                 ret = _pthread_cond_init(&prwlock->read_signal, NULL);
62
63                 if (ret != 0) {
64                         _pthread_mutex_destroy(&prwlock->lock);
65                         free(prwlock);
66                 } else {
67                         /* initialize the write condition signal */
68                         ret = _pthread_cond_init(&prwlock->write_signal, NULL);
69
70                         if (ret != 0) {
71                                 _pthread_cond_destroy(&prwlock->read_signal);
72                                 _pthread_mutex_destroy(&prwlock->lock);
73                                 free(prwlock);
74                         } else {
75                                 /* success */
76                                 prwlock->state = 0;
77                                 prwlock->blocked_writers = 0;
78                                 *rwlock = prwlock;
79                         }
80                 }
81         }
82
83         return (ret);
84 }
85
86 int
87 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
88 {
89         int ret;
90
91         if (rwlock == NULL)
92                 ret = EINVAL;
93         else {
94                 pthread_rwlock_t prwlock;
95
96                 prwlock = *rwlock;
97
98                 _pthread_mutex_destroy(&prwlock->lock);
99                 _pthread_cond_destroy(&prwlock->read_signal);
100                 _pthread_cond_destroy(&prwlock->write_signal);
101                 free(prwlock);
102
103                 *rwlock = NULL;
104
105                 ret = 0;
106         }
107         return (ret);
108 }
109
110 static int
111 init_static(struct pthread *thread, pthread_rwlock_t *rwlock)
112 {
113         int ret;
114
115         THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
116
117         if (*rwlock == NULL)
118                 ret = rwlock_init(rwlock, NULL);
119         else
120                 ret = 0;
121
122         THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
123
124         return (ret);
125 }
126
127 int
128 _pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
129 {
130         *rwlock = NULL;
131         return (rwlock_init(rwlock, attr));
132 }
133
134 static int
135 rwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
136 {
137         struct pthread *curthread = tls_get_curthread();
138         pthread_rwlock_t prwlock;
139         int ret;
140
141         if (rwlock == NULL)
142                 return (EINVAL);
143
144         prwlock = *rwlock;
145
146         /* check for static initialization */
147         if (prwlock == NULL) {
148                 if ((ret = init_static(curthread, rwlock)) != 0)
149                         return (ret);
150
151                 prwlock = *rwlock;
152         }
153
154         /* grab the monitor lock */
155         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
156                 return (ret);
157
158         /* check lock count */
159         if (prwlock->state == MAX_READ_LOCKS) {
160                 _pthread_mutex_unlock(&prwlock->lock);
161                 return (EAGAIN);
162         }
163
164         curthread = tls_get_curthread();
165         if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
166                 /*
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 ;-)
177                  */
178                 ;       /* nothing needed */
179         } else {
180                 /* give writers priority over readers */
181                 while (prwlock->blocked_writers || prwlock->state < 0) {
182                         if (abstime)
183                                 ret = _pthread_cond_timedwait
184                                     (&prwlock->read_signal,
185                                     &prwlock->lock, abstime);
186                         else
187                                 ret = _pthread_cond_wait(&prwlock->read_signal,
188                             &prwlock->lock);
189                         if (ret != 0) {
190                                 /* can't do a whole lot if this fails */
191                                 _pthread_mutex_unlock(&prwlock->lock);
192                                 return (ret);
193                         }
194                 }
195         }
196
197         curthread->rdlock_count++;
198         prwlock->state++; /* indicate we are locked for reading */
199
200         /*
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.
205          */
206         _pthread_mutex_unlock(&prwlock->lock);
207
208         return (ret);
209 }
210
211 int
212 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
213 {
214         return (rwlock_rdlock_common(rwlock, NULL));
215 }
216
217 int
218 _pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
219          const struct timespec *abstime)
220 {
221         return (rwlock_rdlock_common(rwlock, abstime));
222 }
223
224 int
225 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
226 {
227         struct pthread *curthread = tls_get_curthread();
228         pthread_rwlock_t prwlock;
229         int ret;
230
231         if (rwlock == NULL)
232                 return (EINVAL);
233
234         prwlock = *rwlock;
235
236         /* check for static initialization */
237         if (prwlock == NULL) {
238                 if ((ret = init_static(curthread, rwlock)) != 0)
239                         return (ret);
240
241                 prwlock = *rwlock;
242         }
243
244         /* grab the monitor lock */
245         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
246                 return (ret);
247
248         curthread = tls_get_curthread();
249         if (prwlock->state == MAX_READ_LOCKS)
250                 ret = EAGAIN;
251         else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
252                 /* see comment for pthread_rwlock_rdlock() */
253                 curthread->rdlock_count++;
254                 prwlock->state++;
255         }
256         /* give writers priority over readers */
257         else if (prwlock->blocked_writers || prwlock->state < 0)
258                 ret = EBUSY;
259         else {
260                 curthread->rdlock_count++;
261                 prwlock->state++; /* indicate we are locked for reading */
262         }
263
264         /* see the comment on this in pthread_rwlock_rdlock */
265         _pthread_mutex_unlock(&prwlock->lock);
266
267         return (ret);
268 }
269
270 int
271 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
272 {
273         struct pthread *curthread = tls_get_curthread();
274         pthread_rwlock_t prwlock;
275         int ret;
276
277         if (rwlock == NULL)
278                 return (EINVAL);
279
280         prwlock = *rwlock;
281
282         /* check for static initialization */
283         if (prwlock == NULL) {
284                 if ((ret = init_static(curthread, rwlock)) != 0)
285                         return (ret);
286
287                 prwlock = *rwlock;
288         }
289
290         /* grab the monitor lock */
291         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
292                 return (ret);
293
294         if (prwlock->state != 0)
295                 ret = EBUSY;
296         else
297                 /* indicate we are locked for writing */
298                 prwlock->state = -1;
299
300         /* see the comment on this in pthread_rwlock_rdlock */
301         _pthread_mutex_unlock(&prwlock->lock);
302
303         return (ret);
304 }
305
306 int
307 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
308 {
309         struct pthread *curthread;
310         pthread_rwlock_t prwlock;
311         int ret;
312
313         if (rwlock == NULL)
314                 return (EINVAL);
315
316         prwlock = *rwlock;
317
318         if (prwlock == NULL)
319                 return (EINVAL);
320
321         /* grab the monitor lock */
322         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
323                 return (ret);
324
325         curthread = tls_get_curthread();
326         if (prwlock->state > 0) {
327                 curthread->rdlock_count--;
328                 prwlock->state--;
329                 if (prwlock->state == 0 && prwlock->blocked_writers)
330                         ret = _pthread_cond_signal(&prwlock->write_signal);
331         } else if (prwlock->state < 0) {
332                 prwlock->state = 0;
333
334                 if (prwlock->blocked_writers)
335                         ret = _pthread_cond_signal(&prwlock->write_signal);
336                 else
337                         ret = _pthread_cond_broadcast(&prwlock->read_signal);
338         } else
339                 ret = EINVAL;
340
341         /* see the comment on this in pthread_rwlock_rdlock */
342         _pthread_mutex_unlock(&prwlock->lock);
343
344         return (ret);
345 }
346
347 static int
348 rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
349 {
350         struct pthread *curthread = tls_get_curthread();
351         pthread_rwlock_t prwlock;
352         int ret;
353
354         if (rwlock == NULL)
355                 return (EINVAL);
356
357         prwlock = *rwlock;
358
359         /* check for static initialization */
360         if (prwlock == NULL) {
361                 if ((ret = init_static(curthread, rwlock)) != 0)
362                         return (ret);
363
364                 prwlock = *rwlock;
365         }
366
367         /* grab the monitor lock */
368         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
369                 return (ret);
370
371         while (prwlock->state != 0) {
372                 prwlock->blocked_writers++;
373
374                 if (abstime != NULL)
375                         ret = _pthread_cond_timedwait(&prwlock->write_signal,
376                             &prwlock->lock, abstime);
377                 else
378                         ret = _pthread_cond_wait(&prwlock->write_signal,
379                             &prwlock->lock);
380                 if (ret != 0) {
381                         prwlock->blocked_writers--;
382                         _pthread_mutex_unlock(&prwlock->lock);
383                         return (ret);
384                 }
385
386                 prwlock->blocked_writers--;
387         }
388
389         /* indicate we are locked for writing */
390         prwlock->state = -1;
391
392         /* see the comment on this in pthread_rwlock_rdlock */
393         _pthread_mutex_unlock(&prwlock->lock);
394
395         return (ret);
396 }
397
398 int
399 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
400 {
401         return (rwlock_wrlock_common (rwlock, NULL));
402 }
403
404 int
405 _pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
406     const struct timespec *abstime)
407 {
408         return (rwlock_wrlock_common (rwlock, abstime));
409 }
410
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);
420