pthread_rwlock.c revision 1.25 1 /* $NetBSD: pthread_rwlock.c,v 1.25 2008/01/31 10:07:09 mlelstv Exp $ */
2
3 /*-
4 * Copyright (c) 2002, 2006, 2007 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nathan J. Williams and Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 #include <sys/cdefs.h>
40 __RCSID("$NetBSD: pthread_rwlock.c,v 1.25 2008/01/31 10:07:09 mlelstv Exp $");
41
42 #include <errno.h>
43
44 #include "pthread.h"
45 #include "pthread_int.h"
46
47 #ifndef PTHREAD__HAVE_ATOMIC
48
49 __weak_alias(pthread_rwlock_held_np,_pthread_rwlock_held_np)
50 __weak_alias(pthread_rwlock_rdheld_np,_pthread_rwlock_rdheld_np)
51 __weak_alias(pthread_rwlock_wrheld_np,_pthread_rwlock_wrheld_np)
52
53 int _pthread_rwlock_held_np(pthread_rwlock_t *);
54 int _pthread_rwlock_rdheld_np(pthread_rwlock_t *);
55 int _pthread_rwlock_wrheld_np(pthread_rwlock_t *);
56
57 __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
58 __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
59 __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
60 __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
61 __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
62 __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
63 __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
64
65 int
66 pthread_rwlock_init(pthread_rwlock_t *rwlock,
67 const pthread_rwlockattr_t *attr)
68 {
69 #ifdef ERRORCHECK
70 if ((rwlock == NULL) ||
71 (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
72 return EINVAL;
73 #endif
74 rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
75 pthread_lockinit(&rwlock->ptr_interlock);
76 PTQ_INIT(&rwlock->ptr_rblocked);
77 PTQ_INIT(&rwlock->ptr_wblocked);
78 rwlock->ptr_nreaders = 0;
79 rwlock->ptr_writer = NULL;
80
81 return 0;
82 }
83
84
85 int
86 pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
87 {
88 #ifdef ERRORCHECK
89 if ((rwlock == NULL) ||
90 (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
91 (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
92 (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
93 (rwlock->ptr_nreaders != 0) ||
94 (rwlock->ptr_writer != NULL))
95 return EINVAL;
96 #endif
97 rwlock->ptr_magic = _PT_RWLOCK_DEAD;
98
99 return 0;
100 }
101
102
103 int
104 pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
105 {
106 pthread_t self;
107 #ifdef ERRORCHECK
108 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
109 return EINVAL;
110 #endif
111 self = pthread__self();
112
113 pthread__spinlock(self, &rwlock->ptr_interlock);
114 #ifdef ERRORCHECK
115 if (rwlock->ptr_writer == self) {
116 pthread__spinunlock(self, &rwlock->ptr_interlock);
117 return EDEADLK;
118 }
119 #endif
120 /*
121 * Don't get a readlock if there is a writer or if there are waiting
122 * writers; i.e. prefer writers to readers. This strategy is dictated
123 * by SUSv3.
124 */
125 while ((rwlock->ptr_writer != NULL) ||
126 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
127 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
128 self->pt_sleeponq = 1;
129 self->pt_sleepobj = &rwlock->ptr_rblocked;
130 pthread__spinunlock(self, &rwlock->ptr_interlock);
131 (void)pthread__park(self, &rwlock->ptr_interlock,
132 &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked);
133 pthread__spinlock(self, &rwlock->ptr_interlock);
134 }
135
136 rwlock->ptr_nreaders++;
137 pthread__spinunlock(self, &rwlock->ptr_interlock);
138
139 return 0;
140 }
141
142
143 int
144 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
145 {
146 pthread_t self;
147
148 #ifdef ERRORCHECK
149 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
150 return EINVAL;
151 #endif
152
153 self = pthread__self();
154 pthread__spinlock(self, &rwlock->ptr_interlock);
155 /*
156 * Don't get a readlock if there is a writer or if there are waiting
157 * writers; i.e. prefer writers to readers. This strategy is dictated
158 * by SUSv3.
159 */
160 if ((rwlock->ptr_writer != NULL) ||
161 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
162 pthread__spinunlock(self, &rwlock->ptr_interlock);
163 return EBUSY;
164 }
165
166 rwlock->ptr_nreaders++;
167 pthread__spinunlock(self, &rwlock->ptr_interlock);
168
169 return 0;
170 }
171
172
173 int
174 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
175 {
176 pthread_t self;
177 extern int pthread__started;
178
179 #ifdef ERRORCHECK
180 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
181 return EINVAL;
182 #endif
183 self = pthread__self();
184
185 pthread__spinlock(self, &rwlock->ptr_interlock);
186 #ifdef ERRORCHECK
187 if (rwlock->ptr_writer == self) {
188 pthread__spinunlock(self, &rwlock->ptr_interlock);
189 return EDEADLK;
190 }
191 #endif
192 /*
193 * Prefer writers to readers here; permit writers even if there are
194 * waiting readers.
195 */
196 while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
197 #ifdef ERRORCHECK
198 if (pthread__started == 0) {
199 pthread__spinunlock(self, &rwlock->ptr_interlock);
200 return EDEADLK;
201 }
202 #endif
203 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
204 self->pt_sleeponq = 1;
205 self->pt_sleepobj = &rwlock->ptr_wblocked;
206 pthread__spinunlock(self, &rwlock->ptr_interlock);
207 (void)pthread__park(self, &rwlock->ptr_interlock,
208 &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked);
209 pthread__spinlock(self, &rwlock->ptr_interlock);
210 }
211
212 rwlock->ptr_writer = self;
213 pthread__spinunlock(self, &rwlock->ptr_interlock);
214
215 return 0;
216 }
217
218
219 int
220 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
221 {
222 pthread_t self;
223 #ifdef ERRORCHECK
224 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
225 return EINVAL;
226 #endif
227 self = pthread__self();
228
229 pthread__spinlock(self, &rwlock->ptr_interlock);
230 /*
231 * Prefer writers to readers here; permit writers even if there are
232 * waiting readers.
233 */
234 if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
235 pthread__spinunlock(self, &rwlock->ptr_interlock);
236 return EBUSY;
237 }
238
239 rwlock->ptr_writer = self;
240 pthread__spinunlock(self, &rwlock->ptr_interlock);
241
242 return 0;
243 }
244
245
246 int
247 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
248 const struct timespec *abs_timeout)
249 {
250 pthread_t self;
251 int retval;
252
253 #ifdef ERRORCHECK
254 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
255 return EINVAL;
256 if (abs_timeout == NULL)
257 return EINVAL;
258 #endif
259 if ((abs_timeout->tv_nsec >= 1000000000) ||
260 (abs_timeout->tv_nsec < 0) ||
261 (abs_timeout->tv_sec < 0))
262 return EINVAL;
263
264 self = pthread__self();
265 pthread__spinlock(self, &rwlock->ptr_interlock);
266 #ifdef ERRORCHECK
267 if (rwlock->ptr_writer == self) {
268 pthread__spinunlock(self, &rwlock->ptr_interlock);
269 return EDEADLK;
270 }
271 #endif
272 /*
273 * Don't get a readlock if there is a writer or if there are waiting
274 * writers; i.e. prefer writers to readers. This strategy is dictated
275 * by SUSv3.
276 */
277 retval = 0;
278 while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
279 (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
280 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
281 self->pt_sleeponq = 1;
282 self->pt_sleepobj = &rwlock->ptr_rblocked;
283 pthread__spinunlock(self, &rwlock->ptr_interlock);
284 retval = pthread__park(self, &rwlock->ptr_interlock,
285 &rwlock->ptr_rblocked, abs_timeout, 0,
286 &rwlock->ptr_rblocked);
287 pthread__spinlock(self, &rwlock->ptr_interlock);
288 }
289
290 /* One last chance to get the lock, in case it was released between
291 the alarm firing and when this thread got rescheduled, or in case
292 a signal handler kept it busy */
293 if ((rwlock->ptr_writer == NULL) &&
294 (PTQ_EMPTY(&rwlock->ptr_wblocked))) {
295 rwlock->ptr_nreaders++;
296 retval = 0;
297 }
298 pthread__spinunlock(self, &rwlock->ptr_interlock);
299
300 return retval;
301 }
302
303
304 int
305 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
306 const struct timespec *abs_timeout)
307 {
308 pthread_t self;
309 int retval;
310 extern int pthread__started;
311
312 #ifdef ERRORCHECK
313 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
314 return EINVAL;
315 if (abs_timeout == NULL)
316 return EINVAL;
317 #endif
318 if ((abs_timeout->tv_nsec >= 1000000000) ||
319 (abs_timeout->tv_nsec < 0) ||
320 (abs_timeout->tv_sec < 0))
321 return EINVAL;
322
323 self = pthread__self();
324 pthread__spinlock(self, &rwlock->ptr_interlock);
325 #ifdef ERRORCHECK
326 if (rwlock->ptr_writer == self) {
327 pthread__spinunlock(self, &rwlock->ptr_interlock);
328 return EDEADLK;
329 }
330 #endif
331 /*
332 * Prefer writers to readers here; permit writers even if there are
333 * waiting readers.
334 */
335 retval = 0;
336 while (retval == 0 &&
337 ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
338 #ifdef ERRORCHECK
339 if (pthread__started == 0) {
340 pthread__spinunlock(self, &rwlock->ptr_interlock);
341 return EDEADLK;
342 }
343 #endif
344 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
345 self->pt_sleeponq = 1;
346 self->pt_sleepobj = &rwlock->ptr_wblocked;
347 pthread__spinunlock(self, &rwlock->ptr_interlock);
348 retval = pthread__park(self, &rwlock->ptr_interlock,
349 &rwlock->ptr_wblocked, abs_timeout, 0,
350 &rwlock->ptr_wblocked);
351 pthread__spinlock(self, &rwlock->ptr_interlock);
352 }
353
354 if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
355 rwlock->ptr_writer = self;
356 retval = 0;
357 }
358 pthread__spinunlock(self, &rwlock->ptr_interlock);
359
360 return retval;
361 }
362
363
364 int
365 pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
366 {
367 pthread_t self, writer;
368 #ifdef ERRORCHECK
369 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
370 return EINVAL;
371 #endif
372 writer = NULL;
373 self = pthread__self();
374
375 pthread__spinlock(self, &rwlock->ptr_interlock);
376 if (rwlock->ptr_writer != NULL) {
377 /* Releasing a write lock. */
378 #ifdef ERRORCHECK
379 if (rwlock->ptr_writer != self) {
380 pthread__spinunlock(self, &rwlock->ptr_interlock);
381 return EPERM;
382 }
383 #endif
384 rwlock->ptr_writer = NULL;
385 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
386 if (writer != NULL) {
387 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
388 }
389 } else
390 #ifdef ERRORCHECK
391 if (rwlock->ptr_nreaders > 0)
392 #endif
393 {
394 /* Releasing a read lock. */
395 rwlock->ptr_nreaders--;
396 if (rwlock->ptr_nreaders == 0) {
397 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
398 if (writer != NULL)
399 PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
400 pt_sleep);
401 }
402 #ifdef ERRORCHECK
403 } else {
404 pthread__spinunlock(self, &rwlock->ptr_interlock);
405 return EPERM;
406 #endif
407 }
408
409 if (writer != NULL)
410 pthread__unpark(self, &rwlock->ptr_interlock,
411 &rwlock->ptr_wblocked, writer);
412 else
413 pthread__unpark_all(self, &rwlock->ptr_interlock,
414 &rwlock->ptr_rblocked);
415
416 return 0;
417 }
418
419
420 int
421 pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
422 {
423 #ifdef ERRORCHECK
424 if (attr == NULL)
425 return EINVAL;
426 #endif
427 attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
428
429 return 0;
430 }
431
432
433 int
434 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
435 {
436 #ifdef ERRORCHECK
437 if ((attr == NULL) ||
438 (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
439 return EINVAL;
440 #endif
441 attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
442
443 return 0;
444 }
445
446 int
447 _pthread_rwlock_held_np(pthread_rwlock_t *ptr)
448 {
449
450 return ptr->ptr_writer != NULL || ptr->ptr_nreaders != 0;
451 }
452
453 int
454 _pthread_rwlock_rdheld_np(pthread_rwlock_t *ptr)
455 {
456
457 return ptr->ptr_nreaders != 0;
458 }
459
460 int
461 _pthread_rwlock_wrheld_np(pthread_rwlock_t *ptr)
462 {
463
464 return ptr->ptr_writer != 0;
465 }
466
467 #endif /* !PTHREAD__HAVE_ATOMIC */
468