]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - lib/libkse/thread/thr_rwlock.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / lib / libkse / 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$
27  */
28
29 #include "namespace.h"
30 #include <errno.h>
31 #include <limits.h>
32 #include <stdlib.h>
33 #include <pthread.h>
34 #include "un-namespace.h"
35 #include "thr_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_timedrdlock, pthread_rwlock_timedrdlock);
44 __weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
45 __weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
46 __weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
47 __weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
48 __weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);
49
50 /*
51  * Prototypes
52  */
53 static int init_static(pthread_rwlock_t *rwlock);
54
55
56 static int
57 init_static(pthread_rwlock_t *rwlock)
58 {
59         struct pthread *thread = _get_curthread();
60         int ret;
61
62         THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
63
64         if (*rwlock == NULL)
65                 ret = _pthread_rwlock_init(rwlock, NULL);
66         else
67                 ret = 0;
68
69         THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
70         return (ret);
71 }
72
73 int
74 _pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
75 {
76         int ret;
77
78         if (rwlock == NULL)
79                 ret = EINVAL;
80         else {
81                 pthread_rwlock_t prwlock;
82
83                 prwlock = *rwlock;
84
85                 _pthread_mutex_destroy(&prwlock->lock);
86                 _pthread_cond_destroy(&prwlock->read_signal);
87                 _pthread_cond_destroy(&prwlock->write_signal);
88                 free(prwlock);
89
90                 *rwlock = NULL;
91
92                 ret = 0;
93         }
94         return (ret);
95 }
96
97 int
98 _pthread_rwlock_init (pthread_rwlock_t *rwlock,
99     const pthread_rwlockattr_t *attr __unused)
100 {
101         pthread_rwlock_t prwlock;
102         int ret;
103
104         /* allocate rwlock object */
105         prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
106
107         if (prwlock == NULL)
108                 return (ENOMEM);
109
110         /* initialize the lock */
111         if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0)
112                 free(prwlock);
113         else {
114                 /* initialize the read condition signal */
115                 ret = _pthread_cond_init(&prwlock->read_signal, NULL);
116
117                 if (ret != 0) {
118                         _pthread_mutex_destroy(&prwlock->lock);
119                         free(prwlock);
120                 } else {
121                         /* initialize the write condition signal */
122                         ret = _pthread_cond_init(&prwlock->write_signal, NULL);
123
124                         if (ret != 0) {
125                                 _pthread_cond_destroy(&prwlock->read_signal);
126                                 _pthread_mutex_destroy(&prwlock->lock);
127                                 free(prwlock);
128                         } else {
129                                 /* success */
130                                 prwlock->state = 0;
131                                 prwlock->blocked_writers = 0;
132
133                                 *rwlock = prwlock;
134                         }
135                 }
136         }
137
138         return (ret);
139 }
140
141 static int
142 rwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
143 {
144         pthread_rwlock_t prwlock;
145         struct pthread *curthread;
146         int ret;
147
148         if (rwlock == NULL)
149                 return (EINVAL);
150
151         prwlock = *rwlock;
152
153         /* check for static initialization */
154         if (prwlock == NULL) {
155                 if ((ret = init_static(rwlock)) != 0)
156                         return (ret);
157
158                 prwlock = *rwlock;
159         }
160
161         /* grab the monitor lock */
162         if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
163                 return (ret);
164
165         /* check lock count */
166         if (prwlock->state == MAX_READ_LOCKS) {
167                 _thr_mutex_unlock(&prwlock->lock);
168                 return (EAGAIN);
169         }
170
171         curthread = _get_curthread();
172         if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
173                 /*
174                  * To avoid having to track all the rdlocks held by
175                  * a thread or all of the threads that hold a rdlock,
176                  * we keep a simple count of all the rdlocks held by
177                  * a thread.  If a thread holds any rdlocks it is
178                  * possible that it is attempting to take a recursive
179                  * rdlock.  If there are blocked writers and precedence
180                  * is given to them, then that would result in the thread
181                  * deadlocking.  So allowing a thread to take the rdlock
182                  * when it already has one or more rdlocks avoids the
183                  * deadlock.  I hope the reader can follow that logic ;-)
184                  */
185                 ;       /* nothing needed */
186         } else {
187                 /* give writers priority over readers */
188                 while (prwlock->blocked_writers || prwlock->state < 0) {
189                         if (abstime)
190                                 ret = _pthread_cond_timedwait
191                                     (&prwlock->read_signal,
192                                     &prwlock->lock, abstime);
193                         else
194                                 ret = _thr_cond_wait(&prwlock->read_signal,
195                             &prwlock->lock);
196                         if (ret != 0) {
197                                 /* can't do a whole lot if this fails */
198                                 _thr_mutex_unlock(&prwlock->lock);
199                                 return (ret);
200                         }
201                 }
202         }
203
204         curthread->rdlock_count++;
205         prwlock->state++; /* indicate we are locked for reading */
206
207         /*
208          * Something is really wrong if this call fails.  Returning
209          * error won't do because we've already obtained the read
210          * lock.  Decrementing 'state' is no good because we probably
211          * don't have the monitor lock.
212          */
213         _thr_mutex_unlock(&prwlock->lock);
214
215         return (ret);
216 }
217
218 int
219 _pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
220 {
221         return (rwlock_rdlock_common(rwlock, NULL));
222 }
223
224 __strong_reference(_pthread_rwlock_rdlock, _thr_rwlock_rdlock);
225
226 int
227 _pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
228          const struct timespec *abstime)
229 {
230         return (rwlock_rdlock_common(rwlock, abstime));
231 }
232
233 int
234 _pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
235 {
236         struct pthread *curthread;
237         pthread_rwlock_t prwlock;
238         int ret;
239
240         if (rwlock == NULL)
241                 return (EINVAL);
242
243         prwlock = *rwlock;
244
245         /* check for static initialization */
246         if (prwlock == NULL) {
247                 if ((ret = init_static(rwlock)) != 0)
248                         return (ret);
249
250                 prwlock = *rwlock;
251         }
252
253         /* grab the monitor lock */
254         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
255                 return (ret);
256
257         curthread = _get_curthread();
258         if (prwlock->state == MAX_READ_LOCKS)
259                 ret = EAGAIN;
260         else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
261                 /* see comment for pthread_rwlock_rdlock() */
262                 curthread->rdlock_count++;
263                 prwlock->state++;
264         }
265         /* give writers priority over readers */
266         else if (prwlock->blocked_writers || prwlock->state < 0)
267                 ret = EBUSY;
268         else {
269                 curthread->rdlock_count++;
270                 prwlock->state++; /* indicate we are locked for reading */
271         }
272
273         /* see the comment on this in pthread_rwlock_rdlock */
274         _pthread_mutex_unlock(&prwlock->lock);
275
276         return (ret);
277 }
278
279 int
280 _pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
281 {
282         pthread_rwlock_t prwlock;
283         int ret;
284
285         if (rwlock == NULL)
286                 return (EINVAL);
287
288         prwlock = *rwlock;
289
290         /* check for static initialization */
291         if (prwlock == NULL) {
292                 if ((ret = init_static(rwlock)) != 0)
293                         return (ret);
294
295                 prwlock = *rwlock;
296         }
297
298         /* grab the monitor lock */
299         if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
300                 return (ret);
301
302         if (prwlock->state != 0)
303                 ret = EBUSY;
304         else
305                 /* indicate we are locked for writing */
306                 prwlock->state = -1;
307
308         /* see the comment on this in pthread_rwlock_rdlock */
309         _pthread_mutex_unlock(&prwlock->lock);
310
311         return (ret);
312 }
313
314 int
315 _pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
316 {
317         struct pthread *curthread;
318         pthread_rwlock_t prwlock;
319         int ret;
320
321         if (rwlock == NULL)
322                 return (EINVAL);
323
324         prwlock = *rwlock;
325
326         if (prwlock == NULL)
327                 return (EINVAL);
328
329         /* grab the monitor lock */
330         if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
331                 return (ret);
332
333         curthread = _get_curthread();
334         if (prwlock->state > 0) {
335                 curthread->rdlock_count--;
336                 prwlock->state--;
337                 if (prwlock->state == 0 && prwlock->blocked_writers)
338                         ret = _thr_cond_signal(&prwlock->write_signal);
339         } else if (prwlock->state < 0) {
340                 prwlock->state = 0;
341
342                 if (prwlock->blocked_writers)
343                         ret = _thr_cond_signal(&prwlock->write_signal);
344                 else
345                         ret = _thr_cond_broadcast(&prwlock->read_signal);
346         } else
347                 ret = EINVAL;
348
349         /* see the comment on this in pthread_rwlock_rdlock */
350         _thr_mutex_unlock(&prwlock->lock);
351
352         return (ret);
353 }
354
355 __strong_reference(_pthread_rwlock_unlock, _thr_rwlock_unlock);
356
357 static int
358 rwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
359 {
360         pthread_rwlock_t prwlock;
361         int ret;
362
363         if (rwlock == NULL)
364                 return (EINVAL);
365
366         prwlock = *rwlock;
367
368         /* check for static initialization */
369         if (prwlock == NULL) {
370                 if ((ret = init_static(rwlock)) != 0)
371                         return (ret);
372
373                 prwlock = *rwlock;
374         }
375
376         /* grab the monitor lock */
377         if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
378                 return (ret);
379
380         while (prwlock->state != 0) {
381                 prwlock->blocked_writers++;
382
383                 if (abstime != NULL)
384                         ret = _pthread_cond_timedwait(&prwlock->write_signal,
385                             &prwlock->lock, abstime);
386                 else
387                         ret = _thr_cond_wait(&prwlock->write_signal,
388                             &prwlock->lock);
389                 if (ret != 0) {
390                         prwlock->blocked_writers--;
391                         _thr_mutex_unlock(&prwlock->lock);
392                         return (ret);
393                 }
394
395                 prwlock->blocked_writers--;
396         }
397
398         /* indicate we are locked for writing */
399         prwlock->state = -1;
400
401         /* see the comment on this in pthread_rwlock_rdlock */
402         _thr_mutex_unlock(&prwlock->lock);
403
404         return (ret);
405 }
406
407 int
408 _pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
409 {
410         return (rwlock_wrlock_common (rwlock, NULL));
411 }
412 __strong_reference(_pthread_rwlock_wrlock, _thr_rwlock_wrlock);
413
414 int
415 _pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
416     const struct timespec *abstime)
417 {
418         return (rwlock_wrlock_common (rwlock, abstime));
419 }