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