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