pthread_rwlock.c revision 1.18 1 /* $NetBSD: pthread_rwlock.c,v 1.18 2007/03/24 18:52:00 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.18 2007/03/24 18:52:00 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 (void)pthread__park(self, &rwlock->ptr_interlock,
121 &rwlock->ptr_rblocked, NULL, 0, &rwlock->ptr_rblocked);
122 }
123
124 rwlock->ptr_nreaders++;
125 pthread_spinunlock(self, &rwlock->ptr_interlock);
126
127 return 0;
128 }
129
130
131 int
132 pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
133 {
134 pthread_t self;
135 #ifdef ERRORCHECK
136 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
137 return EINVAL;
138 #endif
139 self = pthread__self();
140
141 pthread_spinlock(self, &rwlock->ptr_interlock);
142 /*
143 * Don't get a readlock if there is a writer or if there are waiting
144 * writers; i.e. prefer writers to readers. This strategy is dictated
145 * by SUSv3.
146 */
147 if ((rwlock->ptr_writer != NULL) ||
148 (!PTQ_EMPTY(&rwlock->ptr_wblocked))) {
149 pthread_spinunlock(self, &rwlock->ptr_interlock);
150 return EBUSY;
151 }
152
153 rwlock->ptr_nreaders++;
154 pthread_spinunlock(self, &rwlock->ptr_interlock);
155
156 return 0;
157 }
158
159
160 int
161 pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
162 {
163 pthread_t self;
164 extern int pthread__started;
165
166 #ifdef ERRORCHECK
167 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
168 return EINVAL;
169 #endif
170 self = pthread__self();
171
172 pthread_spinlock(self, &rwlock->ptr_interlock);
173 #ifdef ERRORCHECK
174 if (rwlock->ptr_writer == self) {
175 pthread_spinunlock(self, &rwlock->ptr_interlock);
176 return EDEADLK;
177 }
178 #endif
179 /*
180 * Prefer writers to readers here; permit writers even if there are
181 * waiting readers.
182 */
183 while ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
184 #ifdef ERRORCHECK
185 if (pthread__started == 0) {
186 pthread_spinunlock(self, &rwlock->ptr_interlock);
187 return EDEADLK;
188 }
189 #endif
190 PTQ_INSERT_TAIL(&rwlock->ptr_wblocked, self, pt_sleep);
191 self->pt_sleeponq = 1;
192 self->pt_sleepobj = &rwlock->ptr_wblocked;
193 (void)pthread__park(self, &rwlock->ptr_interlock,
194 &rwlock->ptr_wblocked, NULL, 0, &rwlock->ptr_wblocked);
195 }
196
197 rwlock->ptr_writer = self;
198 pthread_spinunlock(self, &rwlock->ptr_interlock);
199
200 return 0;
201 }
202
203
204 int
205 pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
206 {
207 pthread_t self;
208 #ifdef ERRORCHECK
209 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
210 return EINVAL;
211 #endif
212 self = pthread__self();
213
214 pthread_spinlock(self, &rwlock->ptr_interlock);
215 /*
216 * Prefer writers to readers here; permit writers even if there are
217 * waiting readers.
218 */
219 if ((rwlock->ptr_nreaders > 0) || (rwlock->ptr_writer != NULL)) {
220 pthread_spinunlock(self, &rwlock->ptr_interlock);
221 return EBUSY;
222 }
223
224 rwlock->ptr_writer = self;
225 pthread_spinunlock(self, &rwlock->ptr_interlock);
226
227 return 0;
228 }
229
230
231 struct pthread_rwlock__waitarg {
232 pthread_t ptw_thread;
233 pthread_rwlock_t *ptw_rwlock;
234 struct pthread_queue_t *ptw_queue;
235 };
236
237 int
238 pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
239 const struct timespec *abs_timeout)
240 {
241 pthread_t self;
242 int retval;
243
244 #ifdef ERRORCHECK
245 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
246 return EINVAL;
247 if (abs_timeout == NULL)
248 return EINVAL;
249 #endif
250 if ((abs_timeout->tv_nsec >= 1000000000) ||
251 (abs_timeout->tv_nsec < 0) ||
252 (abs_timeout->tv_sec < 0))
253 return EINVAL;
254
255 self = pthread__self();
256 pthread_spinlock(self, &rwlock->ptr_interlock);
257 #ifdef ERRORCHECK
258 if (rwlock->ptr_writer == self) {
259 pthread_spinunlock(self, &rwlock->ptr_interlock);
260 return EDEADLK;
261 }
262 #endif
263 /*
264 * Don't get a readlock if there is a writer or if there are waiting
265 * writers; i.e. prefer writers to readers. This strategy is dictated
266 * by SUSv3.
267 */
268 retval = 0;
269 while ((retval == 0) && ((rwlock->ptr_writer != NULL) ||
270 (!PTQ_EMPTY(&rwlock->ptr_wblocked)))) {
271 PTQ_INSERT_TAIL(&rwlock->ptr_rblocked, self, pt_sleep);
272 self->pt_sleeponq = 1;
273 self->pt_sleepobj = &rwlock->ptr_rblocked;
274 retval = pthread__park(self, &rwlock->ptr_interlock,
275 &rwlock->ptr_rblocked, abs_timeout, 0,
276 &rwlock->ptr_rblocked);
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 retval = pthread__park(self, &rwlock->ptr_interlock,
337 &rwlock->ptr_wblocked, abs_timeout, 0,
338 &rwlock->ptr_wblocked);
339 }
340
341 if ((rwlock->ptr_nreaders == 0) && (rwlock->ptr_writer == NULL)) {
342 rwlock->ptr_writer = self;
343 retval = 0;
344 }
345 pthread_spinunlock(self, &rwlock->ptr_interlock);
346
347 return retval;
348 }
349
350
351 int
352 pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
353 {
354 pthread_t self, writer;
355 #ifdef ERRORCHECK
356 if ((rwlock == NULL) || (rwlock->ptr_magic != _PT_RWLOCK_MAGIC))
357 return EINVAL;
358 #endif
359 writer = NULL;
360 self = pthread__self();
361
362 pthread_spinlock(self, &rwlock->ptr_interlock);
363 if (rwlock->ptr_writer != NULL) {
364 /* Releasing a write lock. */
365 #ifdef ERRORCHECK
366 if (rwlock->ptr_writer != self) {
367 pthread_spinunlock(self, &rwlock->ptr_interlock);
368 return EPERM;
369 }
370 #endif
371 rwlock->ptr_writer = NULL;
372 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
373 if (writer != NULL) {
374 PTQ_REMOVE(&rwlock->ptr_wblocked, writer, pt_sleep);
375 }
376 } else
377 #ifdef ERRORCHECK
378 if (rwlock->ptr_nreaders > 0)
379 #endif
380 {
381 /* Releasing a read lock. */
382 rwlock->ptr_nreaders--;
383 if (rwlock->ptr_nreaders == 0) {
384 writer = PTQ_FIRST(&rwlock->ptr_wblocked);
385 if (writer != NULL)
386 PTQ_REMOVE(&rwlock->ptr_wblocked, writer,
387 pt_sleep);
388 }
389 #ifdef ERRORCHECK
390 } else {
391 pthread_spinunlock(self, &rwlock->ptr_interlock);
392 return EPERM;
393 #endif
394 }
395
396 if (writer != NULL)
397 pthread__unpark(self, &rwlock->ptr_interlock,
398 &rwlock->ptr_wblocked, writer);
399 else
400 pthread__unpark_all(self, &rwlock->ptr_interlock,
401 &rwlock->ptr_rblocked);
402
403 return 0;
404 }
405
406
407 int
408 pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
409 {
410 #ifdef ERRORCHECK
411 if (attr == NULL)
412 return EINVAL;
413 #endif
414 attr->ptra_magic = _PT_RWLOCKATTR_MAGIC;
415
416 return 0;
417 }
418
419
420 int
421 pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
422 {
423 #ifdef ERRORCHECK
424 if ((attr == NULL) ||
425 (attr->ptra_magic != _PT_RWLOCKATTR_MAGIC))
426 return EINVAL;
427 #endif
428 attr->ptra_magic = _PT_RWLOCKATTR_DEAD;
429
430 return 0;
431 }
432