Add the DragonFly cvs id and perform general cleanups on cvs/rcs/sccs ids. Most
[dragonfly.git] / lib / libc_r / uthread / uthread_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/libc_r/uthread/uthread_rwlock.c,v 1.4.2.2 2002/10/22 14:44:03 fjoe Exp $
27  * $DragonFly: src/lib/libc_r/uthread/uthread_rwlock.c,v 1.2 2003/06/17 04:26:48 dillon Exp $
28  */
29
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdlib.h>
33
34 #include <pthread.h>
35 #include "pthread_private.h"
36
37 /* maximum number of times a read lock may be obtained */
38 #define MAX_READ_LOCKS          (INT_MAX - 1)
39
40 __weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
41 __weak_reference(_pthread_rwlock_init, pthread_rwlock_init);
42 __weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
43 __weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
44 __weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
45 __weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
46 __weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
47
48 static int init_static (pthread_rwlock_t *rwlock);
49
50 static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
51
52 static int
53 init_static (pthread_rwlock_t *rwlock)
54 {
55         int ret;
56
57         _SPINLOCK(&static_init_lock);
58
59         if (*rwlock == NULL)
60                 ret = pthread_rwlock_init(rwlock, NULL);
61         else
62                 ret = 0;
63
64         _SPINUNLOCK(&static_init_lock);
65
66         return(ret);
67 }
68
69 int
70 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
71 {
72         int ret;
73
74         if (rwlock == NULL)
75                 ret = EINVAL;
76         else {
77                 pthread_rwlock_t prwlock;
78
79                 prwlock = *rwlock;
80
81                 pthread_mutex_destroy(&prwlock->lock);
82                 pthread_cond_destroy(&prwlock->read_signal);
83                 pthread_cond_destroy(&prwlock->write_signal);
84                 free(prwlock);
85
86                 *rwlock = NULL;
87
88                 ret = 0;
89         }
90
91         return(ret);
92 }
93
94 int
95 _pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
96 {
97         pthread_rwlock_t        prwlock;
98         int                     ret;
99
100         /* allocate rwlock object */
101         prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
102
103         if (prwlock == NULL)
104                 return(ENOMEM);
105
106         /* initialize the lock */
107         if ((ret = pthread_mutex_init(&prwlock->lock, NULL)) != 0)
108                 free(prwlock);
109         else {
110                 /* initialize the read condition signal */
111                 ret = pthread_cond_init(&prwlock->read_signal, NULL);
112
113                 if (ret != 0) {
114                         pthread_mutex_destroy(&prwlock->lock);
115                         free(prwlock);
116                 } else {
117                         /* initialize the write condition signal */
118                         ret = pthread_cond_init(&prwlock->write_signal, NULL);
119
120                         if (ret != 0) {
121                                 pthread_cond_destroy(&prwlock->read_signal);
122                                 pthread_mutex_destroy(&prwlock->lock);
123                                 free(prwlock);
124                         } else {
125                                 /* success */
126                                 prwlock->state           = 0;
127                                 prwlock->blocked_writers = 0;
128
129                                 *rwlock = prwlock;
130                         }
131                 }
132         }
133
134         return(ret);
135 }
136
137 int
138 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
139 {
140         pthread_rwlock_t        prwlock;
141         int                     ret;
142
143         if (rwlock == NULL)
144                 return(EINVAL);
145
146         prwlock = *rwlock;
147
148         /* check for static initialization */
149         if (prwlock == NULL) {
150                 if ((ret = init_static(rwlock)) != 0)
151                         return(ret);
152
153                 prwlock = *rwlock;
154         }
155
156         /* grab the monitor lock */
157         if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
158                 return(ret);
159
160         /* give writers priority over readers */
161         while (prwlock->blocked_writers || prwlock->state < 0) {
162                 ret = pthread_cond_wait(&prwlock->read_signal, &prwlock->lock);
163
164                 if (ret != 0) {
165                         /* can't do a whole lot if this fails */
166                         pthread_mutex_unlock(&prwlock->lock);
167                         return(ret);
168                 }
169         }
170
171         /* check lock count */
172         if (prwlock->state == MAX_READ_LOCKS)
173                 ret = EAGAIN;
174         else
175                 ++prwlock->state; /* indicate we are locked for reading */
176
177         /*
178          * Something is really wrong if this call fails.  Returning
179          * error won't do because we've already obtained the read
180          * lock.  Decrementing 'state' is no good because we probably
181          * don't have the monitor lock.
182          */
183         pthread_mutex_unlock(&prwlock->lock);
184
185         return(ret);
186 }
187
188 int
189 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
190 {
191         pthread_rwlock_t        prwlock;
192         int                     ret;
193
194         if (rwlock == NULL)
195                 return(EINVAL);
196
197         prwlock = *rwlock;
198
199         /* check for static initialization */
200         if (prwlock == NULL) {
201                 if ((ret = init_static(rwlock)) != 0)
202                         return(ret);
203
204                 prwlock = *rwlock;
205         }
206
207         /* grab the monitor lock */
208         if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
209                 return(ret);
210
211         /* give writers priority over readers */
212         if (prwlock->blocked_writers || prwlock->state < 0)
213                 ret = EBUSY;
214         else if (prwlock->state == MAX_READ_LOCKS)
215                 ret = EAGAIN; /* too many read locks acquired */
216         else
217                 ++prwlock->state; /* indicate we are locked for reading */
218
219         /* see the comment on this in pthread_rwlock_rdlock */
220         pthread_mutex_unlock(&prwlock->lock);
221
222         return(ret);
223 }
224
225 int
226 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
227 {
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(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         if (prwlock->state != 0)
249                 ret = EBUSY;
250         else
251                 /* indicate we are locked for writing */
252                 prwlock->state = -1;
253
254         /* see the comment on this in pthread_rwlock_rdlock */
255         pthread_mutex_unlock(&prwlock->lock);
256
257         return(ret);
258 }
259
260 int
261 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
262 {
263         pthread_rwlock_t        prwlock;
264         int                     ret;
265
266         if (rwlock == NULL)
267                 return(EINVAL);
268
269         prwlock = *rwlock;
270
271         if (prwlock == NULL)
272                 return(EINVAL);
273
274         /* grab the monitor lock */
275         if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
276                 return(ret);
277
278         if (prwlock->state > 0) {
279                 if (--prwlock->state == 0 && prwlock->blocked_writers)
280                         ret = pthread_cond_signal(&prwlock->write_signal);
281         } else if (prwlock->state < 0) {
282                 prwlock->state = 0;
283
284                 if (prwlock->blocked_writers)
285                         ret = pthread_cond_signal(&prwlock->write_signal);
286                 else
287                         ret = pthread_cond_broadcast(&prwlock->read_signal);
288         } else
289                 ret = EINVAL;
290
291         /* see the comment on this in pthread_rwlock_rdlock */
292         pthread_mutex_unlock(&prwlock->lock);
293
294         return(ret);
295 }
296
297 int
298 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
299 {
300         pthread_rwlock_t        prwlock;
301         int                     ret;
302
303         if (rwlock == NULL)
304                 return(EINVAL);
305
306         prwlock = *rwlock;
307
308         /* check for static initialization */
309         if (prwlock == NULL) {
310                 if ((ret = init_static(rwlock)) != 0)
311                         return(ret);
312
313                 prwlock = *rwlock;
314         }
315
316         /* grab the monitor lock */
317         if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
318                 return(ret);
319
320         while (prwlock->state != 0) {
321                 ++prwlock->blocked_writers;
322
323                 ret = pthread_cond_wait(&prwlock->write_signal, &prwlock->lock);
324
325                 if (ret != 0) {
326                         --prwlock->blocked_writers;
327                         pthread_mutex_unlock(&prwlock->lock);
328                         return(ret);
329                 }
330
331                 --prwlock->blocked_writers;
332         }
333
334         /* indicate we are locked for writing */
335         prwlock->state = -1;
336
337         /* see the comment on this in pthread_rwlock_rdlock */
338         pthread_mutex_unlock(&prwlock->lock);
339
340         return(ret);
341 }
342