pthread_rwlock.c revision 1.18.2.2 1 /* $NetBSD: pthread_rwlock.c,v 1.18.2.2 2007/09/03 10:14:16 skrll 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.18.2.2 2007/09/03 10:14:16 skrll Exp $");
41
42 #include <errno.h>
43
44 #include "pthread.h"
45 #include "pthread_int.h"
46
47 __strong_alias(__libc_rwlock_init,pthread_rwlock_init)
48 __strong_alias(__libc_rwlock_rdlock,pthread_rwlock_rdlock)
49 __strong_alias(__libc_rwlock_wrlock,pthread_rwlock_wrlock)
50 __strong_alias(__libc_rwlock_tryrdlock,pthread_rwlock_tryrdlock)
51 __strong_alias(__libc_rwlock_trywrlock,pthread_rwlock_trywrlock)
52 __strong_alias(__libc_rwlock_unlock,pthread_rwlock_unlock)
53 __strong_alias(__libc_rwlock_destroy,pthread_rwlock_destroy)
54
55 int
56 pthread_rwlock_init(pthread_rwlock_t *rwlock,
57 const pthread_rwlockattr_t *attr)
58 {
59 #ifdef ERRORCHECK
60 if ((rwlock == NULL) ||
61 (attr && (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC)))
62 return EINVAL;
63 #endif
64 rwlock->ptr_magic = _PT_RWLOCK_MAGIC;
65 pthread_lockinit(&rwlock->ptr_interlock);
66 PTQ_INIT(&rwlock->ptr_rblocked);
67 PTQ_INIT(&rwlock->ptr_wblocked);
68 rwlock->ptr_nreaders = 0;
69 rwlock->ptr_writer = NULL;
70
71 return 0;
72 }
73
74
75 int
76 pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
77 {
78 #ifdef ERRORCHECK
79 if ((rwlock == NULL) ||
80 (rwlock->ptr_magic != _PT_RWLOCK_MAGIC) ||
81 (!PTQ_EMPTY(&rwlock->ptr_rblocked)) ||
82 (!PTQ_EMPTY(&rwlock->ptr_wblocked)) ||
83 (rwlock->ptr_nreaders != 0) ||
84 (rwlock->ptr_writer != NULL))
85 return EINVAL;
86 #endif
87 rwlock->ptr_magic = _PT_RWLOCK_DEAD;
88
89 return 0;
90 }
91
92
93 int
94 pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
95 {
96 pthread_t self;
97 #ifdef ERRORCHECK
98 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
99 return EINVAL;
100 #endif
101 self = pthread__self();
102
103 pthread_spinlock(&rwlock->ptr_interlock);
104 #ifdef ERRORCHECK
105 if (rwlock->ptr_writer == self) {
106 pthread_spinunlock(&rwlock->ptr_interlock);
107 return EDEADLK;
108 }
109 #endif
110 /*
111 * Don't get a readlock if there is a writer or if there are waiting
112 * writers; i.e. prefer writers to readers. This strategy is dictated
113 * by SUSv3.
114 */
115 while ((rwlock->ptr_writer != NULL) ||
116 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
117 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
118 self->pt_sleeponq = 1;
119 self->pt_sleepobj = &rwlock->ptr_rblocked;
120 pthread_spinunlock(&rwlock->ptr_interlock);
121 (void)pthread__park(self, &rwlock->ptr_interlock,
122 &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked);
123 pthread_spinlock(&rwlock->ptr_interlock);
124 }
125
126 rwlock->ptr_nreaders++;
127 pthread_spinunlock(&rwlock->ptr_interlock);
128
129 return 0;
130 }
131
132
133 int
134 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
135 {
136
137 #ifdef ERRORCHECK
138 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
139 return EINVAL;
140 #endif
141
142 pthread_spinlock(&rwlock->ptr_interlock);
143 /*
144 * Don't get a readlock if there is a writer or if there are waiting
145 * writers; i.e. prefer writers to readers. This strategy is dictated
146 * by SUSv3.
147 */
148 if ((rwlock->ptr_writer != NULL) ||
149 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
150 pthread_spinunlock(&rwlock->ptr_interlock);
151 return EBUSY;
152 }
153
154 rwlock->ptr_nreaders++;
155 pthread_spinunlock(&rwlock->ptr_interlock);
156
157 return 0;
158 }
159
160
161 int
162 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
163 {
164 pthread_t self;
165 extern int pthread__started;
166
167 #ifdef ERRORCHECK
168 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
169 return EINVAL;
170 #endif
171 self = pthread__self();
172
173 pthread_spinlock(&rwlock->ptr_interlock);
174 #ifdef ERRORCHECK
175 if (rwlock->ptr_writer == self) {
176 pthread_spinunlock(&rwlock->ptr_interlock);
177 return EDEADLK;
178 }
179 #endif
180 /*
181 * Prefer writers to readers here; permit writers even if there are
182 * waiting readers.
183 */
184 while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
185 #ifdef ERRORCHECK
186 if (pthread__started == 0) {
187 pthread_spinunlock(&rwlock->ptr_interlock);
188 return EDEADLK;
189 }
190 #endif
191 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
192 self->pt_sleeponq = 1;
193 self->pt_sleepobj = &rwlock->ptr_wblocked;
194 pthread_spinunlock(&rwlock->ptr_interlock);
195 (void)pthread__park(self, &rwlock->ptr_interlock,
196 &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked);
197 pthread_spinlock(&rwlock->ptr_interlock);
198 }
199
200 rwlock->ptr_writer = self;
201 pthread_spinunlock(&rwlock->ptr_interlock);
202
203 return 0;
204 }
205
206
207 int
208 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
209 {
210 pthread_t self;
211 #ifdef ERRORCHECK
212 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
213 return EINVAL;
214 #endif
215 self = pthread__self();
216
217 pthread_spinlock(&rwlock->ptr_interlock);
218 /*
219 * Prefer writers to readers here; permit writers even if there are
220 * waiting readers.
221 */
222 if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
223 pthread_spinunlock(&rwlock->ptr_interlock);
224 return EBUSY;
225 }
226
227 rwlock->ptr_writer = self;
228 pthread_spinunlock(&rwlock->ptr_interlock);
229
230 return 0;
231 }
232
233
234 int
235 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
236 const struct timespec *abs_timeout)
237 {
238 pthread_t self;
239 int retval;
240
241 #ifdef ERRORCHECK
242 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
243 return EINVAL;
244 if (abs_timeout == NULL)
245 return EINVAL;
246 #endif
247 if ((abs_timeout->tv_nsec >= 1000000000) ||
248 (abs_timeout->tv_nsec < 0) ||
249 (abs_timeout->tv_sec < 0))
250 return EINVAL;
251
252 self = pthread__self();
253 pthread_spinlock(&rwlock->ptr_interlock);
254 #ifdef ERRORCHECK
255 if (rwlock->ptr_writer == self) {
256 pthread_spinunlock(&rwlock->ptr_interlock);
257 return EDEADLK;
258 }
259 #endif
260 /*
261 * Don't get a readlock if there is a writer or if there are waiting
262 * writers; i.e. prefer writers to readers. This strategy is dictated
263 * by SUSv3.
264 */
265 retval = 0;
266 while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
267 (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
268 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
269 self->pt_sleeponq = 1;
270 self->pt_sleepobj = &rwlock->ptr_rblocked;
271 pthread_spinunlock(&rwlock->ptr_interlock);
272 retval = pthread__park(self, &rwlock->ptr_interlock,
273 &rwlock->ptr_rblocked, abs_timeout, 0,
274 &rwlock->ptr_rblocked);
275 pthread_spinlock(&rwlock->ptr_interlock);
276 }
277
278 /* One last chance to get the lock, in case it was released between
279 the alarm firing and when this thread got rescheduled, or in case
280 a signal handler kept it busy */
281 if ((rwlock->ptr_writer == NULL) &&
282 (PTQ_EMPTY(&rwlock->ptr_wblocked))) {
283 rwlock->ptr_nreaders++;
284 retval = 0;
285 }
286 pthread_spinunlock(&rwlock->ptr_interlock);
287
288 return retval;
289 }
290
291
292 int
293 pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
294 const struct timespec *abs_timeout)
295 {
296 pthread_t self;
297 int retval;
298 extern int pthread__started;
299
300 #ifdef ERRORCHECK
301 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
302 return EINVAL;
303 if (abs_timeout == NULL)
304 return EINVAL;
305 #endif
306 if ((abs_timeout->tv_nsec >= 1000000000) ||
307 (abs_timeout->tv_nsec < 0) ||
308 (abs_timeout->tv_sec < 0))
309 return EINVAL;
310
311 self = pthread__self();
312 pthread_spinlock(&rwlock->ptr_interlock);
313 #ifdef ERRORCHECK
314 if (rwlock->ptr_writer == self) {
315 pthread_spinunlock(&rwlock->ptr_interlock);
316 return EDEADLK;
317 }
318 #endif
319 /*
320 * Prefer writers to readers here; permit writers even if there are
321 * waiting readers.
322 */
323 retval = 0;
324 while (retval == 0 &&
325 ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL))) {
326 #ifdef ERRORCHECK
327 if (pthread__started == 0) {
328 pthread_spinunlock(&rwlock->ptr_interlock);
329 return EDEADLK;
330 }
331 #endif
332 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
333 self->pt_sleeponq = 1;
334 self->pt_sleepobj = &rwlock->ptr_wblocked;
335 pthread_spinunlock(&rwlock->ptr_interlock);
336 retval = pthread__park(self, &rwlock->ptr_interlock,
337 &rwlock->ptr_wblocked, abs_timeout, 0,
338 &rwlock->ptr_wblocked);
339 pthread_spinlock(&rwlock->ptr_interlock);
340 }
341
342 if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
343 rwlock->ptr_writer = self;
344 retval = 0;
345 }
346 pthread_spinunlock(&rwlock->ptr_interlock);
347
348 return retval;
349 }
350
351
352 int
353 pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
354 {
355 pthread_t self, writer;
356 #ifdef ERRORCHECK
357 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
358 return EINVAL;
359 #endif
360 writer = NULL;
361 self = pthread__self();
362
363 pthread_spinlock(&rwlock->ptr_interlock);
364 if (rwlock->ptr_writer != NULL) {
365 /* Releasing a write lock. */
366 #ifdef ERRORCHECK
367 if (rwlock->ptr_writer != self) {
368 pthread_spinunlock(&rwlock->ptr_interlock);
369 return EPERM;
370 }
371 #endif
372 rwlock->ptr_writer = NULL;
373 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
374 if (writer != NULL) {
375 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
376 }
377 } else
378 #ifdef ERRORCHECK
379 if (rwlock->ptr_nreaders > 0)
380 #endif
381 {
382 /* Releasing a read lock. */
383 rwlock->ptr_nreaders--;
384 if (rwlock->ptr_nreaders == 0) {
385 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
386 if (writer != NULL)
387 PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
388 pt_sleep);
389 }
390 #ifdef ERRORCHECK
391 } else {
392 pthread_spinunlock(&rwlock->ptr_interlock);
393 return EPERM;
394 #endif
395 }
396
397 if (writer != NULL)
398 pthread__unpark(self, &rwlock->ptr_interlock,
399 &rwlock->ptr_wblocked, writer);
400 else
401 pthread__unpark_all(self, &rwlock->ptr_interlock,
402 &rwlock->ptr_rblocked);
403
404 return 0;
405 }
406
407
408 int
409 pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
410 {
411 #ifdef ERRORCHECK
412 if (attr == NULL)
413 return EINVAL;
414 #endif
415 attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
416
417 return 0;
418 }
419
420
421 int
422 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
423 {
424 #ifdef ERRORCHECK
425 if ((attr == NULL) ||
426 (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
427 return EINVAL;
428 #endif
429 attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
430
431 return 0;
432 }
433