1 /* $OpenBSD: uthread_rwlock.c,v 1.5 2004/02/01 06:22:14 brad Exp $ */
2 /*-
3 * Copyright (c) 1998 Alex Nash
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 * $FreeBSD: uthread_rwlock.c,v 1.9 2004/01/08 15:39:12 deischen Exp $
28 */
29
30 #ifdef _THREAD_SAFE
31 #include <errno.h>
32 #include <limits.h>
33 #include <stdlib.h>
34
35 #include <pthread.h>
36 #include "pthread_private.h"
37
38 /* maximum number of times a read lock may be obtained */
39 #define MAX_READ_LOCKS (INT_MAX - 1)
40
41 static int init_static (pthread_rwlock_t *rwlock);
42
43 static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
44
45 static int
init_static(pthread_rwlock_t * rwlock)46 init_static (pthread_rwlock_t *rwlock)
47 {
48 int ret;
49
50 _SPINLOCK(&static_init_lock);
51
52 if (*rwlock == NULL)
53 ret = pthread_rwlock_init(rwlock, NULL);
54 else
55 ret = 0;
56
57 _SPINUNLOCK(&static_init_lock);
58
59 return (ret);
60 }
61
62 int
pthread_rwlock_destroy(pthread_rwlock_t * rwlock)63 pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
64 {
65 int ret;
66
67 if (rwlock == NULL)
68 ret = EINVAL;
69 else {
70 pthread_rwlock_t prwlock;
71
72 prwlock = *rwlock;
73
74 pthread_mutex_destroy(&prwlock->lock);
75 pthread_cond_destroy(&prwlock->read_signal);
76 pthread_cond_destroy(&prwlock->write_signal);
77 free(prwlock);
78
79 *rwlock = NULL;
80
81 ret = 0;
82 }
83 return (ret);
84 }
85
86 int
pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr)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
pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)130 pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
131 {
132 pthread_rwlock_t prwlock;
133 struct pthread *curthread;
134 int ret;
135
136 if (rwlock == NULL)
137 return(EINVAL);
138
139 prwlock = *rwlock;
140
141 /* check for static initialization */
142 if (prwlock == NULL) {
143 if ((ret = init_static(rwlock)) != 0)
144 return(ret);
145
146 prwlock = *rwlock;
147 }
148
149 /* grab the monitor lock */
150 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
151 return(ret);
152
153 /* check lock count */
154 if (prwlock->state == MAX_READ_LOCKS) {
155 pthread_mutex_unlock(&prwlock->lock);
156 return (EAGAIN);
157 }
158
159 curthread = _get_curthread();
160 if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
161 /*
162 * To avoid having to track all the rdlocks held by
163 * a thread or all of the threads that hold a rdlock,
164 * we keep a simple count of all the rdlocks held by
165 * a thread. If a thread holds any rdlocks it is
166 * possible that it is attempting to take a recursive
167 * rdlock. If there are blocked writers and precedence
168 * is given to them, then that would result in the thread
169 * deadlocking. So allowing a thread to take the rdlock
170 * when it already has one or more rdlocks avoids the
171 * deadlock. I hope the reader can follow that logic ;-)
172 */
173 ; /* nothing needed */
174 } else {
175 /* give writers priority over readers */
176 while (prwlock->blocked_writers || prwlock->state < 0) {
177 ret = pthread_cond_wait(&prwlock->read_signal,
178 &prwlock->lock);
179
180 if (ret != 0) {
181 /* can't do a whole lot if this fails */
182 pthread_mutex_unlock(&prwlock->lock);
183 return(ret);
184 }
185 }
186 }
187
188 curthread->rdlock_count++;
189 prwlock->state++; /* indicate we are locked for reading */
190
191 /*
192 * Something is really wrong if this call fails. Returning
193 * error won't do because we've already obtained the read
194 * lock. Decrementing 'state' is no good because we probably
195 * don't have the monitor lock.
196 */
197 pthread_mutex_unlock(&prwlock->lock);
198
199 return (ret);
200 }
201
202 int
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)203 pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
204 {
205 pthread_rwlock_t prwlock;
206 struct pthread *curthread;
207 int ret;
208
209 if (rwlock == NULL)
210 return(EINVAL);
211
212 prwlock = *rwlock;
213
214 /* check for static initialization */
215 if (prwlock == NULL) {
216 if ((ret = init_static(rwlock)) != 0)
217 return(ret);
218
219 prwlock = *rwlock;
220 }
221
222 /* grab the monitor lock */
223 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
224 return(ret);
225
226 curthread = _get_curthread();
227 if (prwlock->state == MAX_READ_LOCKS)
228 ret = EAGAIN; /* too many read locks acquired */
229 else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
230 /* see comment for pthread_rwlock_rdlock() */
231 curthread->rdlock_count++;
232 prwlock->state++;
233 }
234 /* give writers priority over readers */
235 else if (prwlock->blocked_writers || prwlock->state < 0)
236 ret = EBUSY;
237 else {
238 prwlock->state++; /* indicate we are locked for reading */
239 curthread->rdlock_count++;
240 }
241
242 /* see the comment on this in pthread_rwlock_rdlock */
243 pthread_mutex_unlock(&prwlock->lock);
244
245 return (ret);
246 }
247
248 int
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)249 pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
250 {
251 pthread_rwlock_t prwlock;
252 int ret;
253
254 if (rwlock == NULL)
255 return(EINVAL);
256
257 prwlock = *rwlock;
258
259 /* check for static initialization */
260 if (prwlock == NULL) {
261 if ((ret = init_static(rwlock)) != 0)
262 return(ret);
263
264 prwlock = *rwlock;
265 }
266
267 /* grab the monitor lock */
268 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
269 return(ret);
270
271 if (prwlock->state != 0)
272 ret = EBUSY;
273 else
274 /* indicate we are locked for writing */
275 prwlock->state = -1;
276
277 /* see the comment on this in pthread_rwlock_rdlock */
278 pthread_mutex_unlock(&prwlock->lock);
279
280 return (ret);
281 }
282
283 int
pthread_rwlock_unlock(pthread_rwlock_t * rwlock)284 pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
285 {
286 pthread_rwlock_t prwlock;
287 struct pthread *curthread;
288 int ret;
289
290 if (rwlock == NULL)
291 return(EINVAL);
292
293 prwlock = *rwlock;
294
295 if (prwlock == NULL)
296 return(EINVAL);
297
298 /* grab the monitor lock */
299 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
300 return(ret);
301
302 curthread = _get_curthread();
303 if (prwlock->state > 0) {
304 curthread->rdlock_count--;
305 prwlock->state--;
306 if (prwlock->state == 0 && prwlock->blocked_writers)
307 ret = pthread_cond_signal(&prwlock->write_signal);
308 } else if (prwlock->state < 0) {
309 prwlock->state = 0;
310
311 if (prwlock->blocked_writers)
312 ret = pthread_cond_signal(&prwlock->write_signal);
313 else
314 ret = pthread_cond_broadcast(&prwlock->read_signal);
315 } else
316 ret = EINVAL;
317
318 /* see the comment on this in pthread_rwlock_rdlock */
319 pthread_mutex_unlock(&prwlock->lock);
320
321 return (ret);
322 }
323
324 int
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)325 pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
326 {
327 pthread_rwlock_t prwlock;
328 int ret;
329
330 if (rwlock == NULL)
331 return(EINVAL);
332
333 prwlock = *rwlock;
334
335 /* check for static initialization */
336 if (prwlock == NULL) {
337 if ((ret = init_static(rwlock)) != 0)
338 return(ret);
339
340 prwlock = *rwlock;
341 }
342
343 /* grab the monitor lock */
344 if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
345 return(ret);
346
347 while (prwlock->state != 0) {
348 prwlock->blocked_writers++;
349
350 ret = pthread_cond_wait(&prwlock->write_signal,
351 &prwlock->lock);
352
353 if (ret != 0) {
354 prwlock->blocked_writers--;
355 pthread_mutex_unlock(&prwlock->lock);
356 return(ret);
357 }
358
359 prwlock->blocked_writers--;
360 }
361
362 /* indicate we are locked for writing */
363 prwlock->state = -1;
364
365 /* see the comment on this in pthread_rwlock_rdlock */
366 pthread_mutex_unlock(&prwlock->lock);
367
368 return (ret);
369 }
370
371 #endif /* _THREAD_SAFE */
372