thr_rwlock.c revision 112918
1/*-
2 * Copyright (c) 1998 Alex Nash
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/lib/libthr/thread/thr_rwlock.c 112918 2003-04-01 03:46:29Z jeff $
27 */
28
29#include <errno.h>
30#include <limits.h>
31#include <stdlib.h>
32
33#include <pthread.h>
34#include "thr_private.h"
35
36/* maximum number of times a read lock may be obtained */
37#define	MAX_READ_LOCKS		(INT_MAX - 1)
38
39__weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
40__weak_reference(_pthread_rwlock_init, pthread_rwlock_init);
41__weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
42__weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
43__weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
44__weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
45__weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
46
47static int init_static (pthread_rwlock_t *rwlock);
48
49static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
50
51static int
52init_static (pthread_rwlock_t *rwlock)
53{
54	int ret;
55
56	_SPINLOCK(&static_init_lock);
57
58	if (*rwlock == NULL)
59		ret = pthread_rwlock_init(rwlock, NULL);
60	else
61		ret = 0;
62
63	_SPINUNLOCK(&static_init_lock);
64
65	return(ret);
66}
67
68int
69_pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
70{
71	int ret;
72
73	if (rwlock == NULL)
74		ret = EINVAL;
75	else {
76		pthread_rwlock_t prwlock;
77
78		prwlock = *rwlock;
79
80		pthread_mutex_destroy(&prwlock->lock);
81		pthread_cond_destroy(&prwlock->read_signal);
82		pthread_cond_destroy(&prwlock->write_signal);
83		free(prwlock);
84
85		*rwlock = NULL;
86
87		ret = 0;
88	}
89
90	return(ret);
91}
92
93int
94_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
95{
96	pthread_rwlock_t	prwlock;
97	int			ret;
98
99	/* allocate rwlock object */
100	prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
101
102	if (prwlock == NULL)
103		return(ENOMEM);
104
105	/* initialize the lock */
106	if ((ret = pthread_mutex_init(&prwlock->lock, NULL)) != 0)
107		free(prwlock);
108	else {
109		/* initialize the read condition signal */
110		ret = pthread_cond_init(&prwlock->read_signal, NULL);
111
112		if (ret != 0) {
113			pthread_mutex_destroy(&prwlock->lock);
114			free(prwlock);
115		} else {
116			/* initialize the write condition signal */
117			ret = pthread_cond_init(&prwlock->write_signal, NULL);
118
119			if (ret != 0) {
120				pthread_cond_destroy(&prwlock->read_signal);
121				pthread_mutex_destroy(&prwlock->lock);
122				free(prwlock);
123			} else {
124				/* success */
125				prwlock->state		 = 0;
126				prwlock->blocked_writers = 0;
127
128				*rwlock = prwlock;
129			}
130		}
131	}
132
133	return(ret);
134}
135
136int
137_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
138{
139	pthread_rwlock_t 	prwlock;
140	int			ret;
141
142	if (rwlock == NULL)
143		return(EINVAL);
144
145	prwlock = *rwlock;
146
147	/* check for static initialization */
148	if (prwlock == NULL) {
149		if ((ret = init_static(rwlock)) != 0)
150			return(ret);
151
152		prwlock = *rwlock;
153	}
154
155	/* grab the monitor lock */
156	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
157		return(ret);
158
159	/* give writers priority over readers */
160	while (prwlock->blocked_writers || prwlock->state < 0) {
161		ret = pthread_cond_wait(&prwlock->read_signal, &prwlock->lock);
162
163		if (ret != 0) {
164			/* can't do a whole lot if this fails */
165			pthread_mutex_unlock(&prwlock->lock);
166			return(ret);
167		}
168	}
169
170	/* check lock count */
171	if (prwlock->state == MAX_READ_LOCKS)
172		ret = EAGAIN;
173	else
174		++prwlock->state; /* indicate we are locked for reading */
175
176	/*
177	 * Something is really wrong if this call fails.  Returning
178	 * error won't do because we've already obtained the read
179	 * lock.  Decrementing 'state' is no good because we probably
180	 * don't have the monitor lock.
181	 */
182	pthread_mutex_unlock(&prwlock->lock);
183
184	return(ret);
185}
186
187int
188_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
189{
190	pthread_rwlock_t 	prwlock;
191	int			ret;
192
193	if (rwlock == NULL)
194		return(EINVAL);
195
196	prwlock = *rwlock;
197
198	/* check for static initialization */
199	if (prwlock == NULL) {
200		if ((ret = init_static(rwlock)) != 0)
201			return(ret);
202
203		prwlock = *rwlock;
204	}
205
206	/* grab the monitor lock */
207	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
208		return(ret);
209
210	/* give writers priority over readers */
211	if (prwlock->blocked_writers || prwlock->state < 0)
212		ret = EBUSY;
213	else if (prwlock->state == MAX_READ_LOCKS)
214		ret = EAGAIN; /* too many read locks acquired */
215	else
216		++prwlock->state; /* indicate we are locked for reading */
217
218	/* see the comment on this in pthread_rwlock_rdlock */
219	pthread_mutex_unlock(&prwlock->lock);
220
221	return(ret);
222}
223
224int
225_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
226{
227	pthread_rwlock_t 	prwlock;
228	int			ret;
229
230	if (rwlock == NULL)
231		return(EINVAL);
232
233	prwlock = *rwlock;
234
235	/* check for static initialization */
236	if (prwlock == NULL) {
237		if ((ret = init_static(rwlock)) != 0)
238			return(ret);
239
240		prwlock = *rwlock;
241	}
242
243	/* grab the monitor lock */
244	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
245		return(ret);
246
247	if (prwlock->state != 0)
248		ret = EBUSY;
249	else
250		/* indicate we are locked for writing */
251		prwlock->state = -1;
252
253	/* see the comment on this in pthread_rwlock_rdlock */
254	pthread_mutex_unlock(&prwlock->lock);
255
256	return(ret);
257}
258
259int
260_pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
261{
262	pthread_rwlock_t 	prwlock;
263	int			ret;
264
265	if (rwlock == NULL)
266		return(EINVAL);
267
268	prwlock = *rwlock;
269
270	if (prwlock == NULL)
271		return(EINVAL);
272
273	/* grab the monitor lock */
274	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
275		return(ret);
276
277	if (prwlock->state > 0) {
278		if (--prwlock->state == 0 && prwlock->blocked_writers)
279			ret = pthread_cond_signal(&prwlock->write_signal);
280	} else if (prwlock->state < 0) {
281		prwlock->state = 0;
282
283		if (prwlock->blocked_writers)
284			ret = pthread_cond_signal(&prwlock->write_signal);
285		else
286			ret = pthread_cond_broadcast(&prwlock->read_signal);
287	} else
288		ret = EINVAL;
289
290	/* see the comment on this in pthread_rwlock_rdlock */
291	pthread_mutex_unlock(&prwlock->lock);
292
293	return(ret);
294}
295
296int
297_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
298{
299	pthread_rwlock_t 	prwlock;
300	int			ret;
301
302	if (rwlock == NULL)
303		return(EINVAL);
304
305	prwlock = *rwlock;
306
307	/* check for static initialization */
308	if (prwlock == NULL) {
309		if ((ret = init_static(rwlock)) != 0)
310			return(ret);
311
312		prwlock = *rwlock;
313	}
314
315	/* grab the monitor lock */
316	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
317		return(ret);
318
319	while (prwlock->state != 0) {
320		++prwlock->blocked_writers;
321
322		ret = pthread_cond_wait(&prwlock->write_signal, &prwlock->lock);
323
324		if (ret != 0) {
325			--prwlock->blocked_writers;
326			pthread_mutex_unlock(&prwlock->lock);
327			return(ret);
328		}
329
330		--prwlock->blocked_writers;
331	}
332
333	/* indicate we are locked for writing */
334	prwlock->state = -1;
335
336	/* see the comment on this in pthread_rwlock_rdlock */
337	pthread_mutex_unlock(&prwlock->lock);
338
339	return(ret);
340}
341
342