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