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