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