thr_rwlock.c revision 38919
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 *	$Id$
27 */
28
29#ifdef _THREAD_SAFE
30#include <errno.h>
31#include <limits.h>
32#include <stdlib.h>
33
34#include <pthread.h>
35#include "pthread_private.h"
36
37/* maximum number of times a read lock may be obtained */
38#define	MAX_READ_LOCKS		(INT_MAX - 1)
39
40static int init_static (pthread_rwlock_t *rwlock);
41
42static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
43
44static int
45init_static (pthread_rwlock_t *rwlock)
46{
47	int ret;
48
49	_SPINLOCK(&static_init_lock);
50
51	if (*rwlock == NULL)
52		ret = pthread_rwlock_init(rwlock, NULL);
53	else
54		ret = 0;
55
56	_SPINUNLOCK(&static_init_lock);
57
58	return(ret);
59}
60
61int
62pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
63{
64	int ret;
65
66	if (rwlock == NULL)
67		ret = EINVAL;
68	else {
69		pthread_rwlock_t prwlock;
70
71		prwlock = *rwlock;
72
73		pthread_mutex_destroy(&prwlock->lock);
74		pthread_cond_destroy(&prwlock->read_signal);
75		pthread_cond_destroy(&prwlock->write_signal);
76		free(prwlock);
77
78		*rwlock = NULL;
79	}
80
81	return(ret);
82}
83
84int
85pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
86{
87	pthread_rwlock_t	prwlock;
88	int			ret;
89
90	/* allocate rwlock object */
91	prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
92
93	if (prwlock == NULL)
94		return(ENOMEM);
95
96	/* initialize the lock */
97	if ((ret = pthread_mutex_init(&prwlock->lock, NULL)) != 0)
98		free(prwlock);
99	else {
100		/* initialize the read condition signal */
101		ret = pthread_cond_init(&prwlock->read_signal, NULL);
102
103		if (ret != 0) {
104			pthread_mutex_destroy(&prwlock->lock);
105			free(prwlock);
106		} else {
107			/* initialize the write condition signal */
108			ret = pthread_cond_init(&prwlock->write_signal, NULL);
109
110			if (ret != 0) {
111				pthread_cond_destroy(&prwlock->read_signal);
112				pthread_mutex_destroy(&prwlock->lock);
113				free(prwlock);
114			} else {
115				/* success */
116				prwlock->state		 = 0;
117				prwlock->blocked_writers = 0;
118
119				*rwlock = prwlock;
120			}
121		}
122	}
123
124	return(ret);
125}
126
127int
128pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
129{
130	pthread_rwlock_t 	prwlock;
131	int			ret = 0;
132
133	if (rwlock == NULL)
134		return(EINVAL);
135
136	prwlock = *rwlock;
137
138	/* check for static initialization */
139	if (prwlock == NULL) {
140		if ((ret = init_static(rwlock)) != 0)
141			return(ret);
142
143		prwlock = *rwlock;
144	}
145
146	/* grab the monitor lock */
147	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
148		return(ret);
149
150	/* give writers priority over readers */
151	while (prwlock->blocked_writers || prwlock->state < 0) {
152		ret = pthread_cond_wait(&prwlock->read_signal, &prwlock->lock);
153
154		if (ret != 0) {
155			/* can't do a whole lot if this fails */
156			pthread_mutex_unlock(&prwlock->lock);
157			return(ret);
158		}
159	}
160
161	/* check lock count */
162	if (prwlock->state == MAX_READ_LOCKS)
163		ret = EAGAIN;
164	else
165		++prwlock->state; /* indicate we are locked for reading */
166
167	/*
168	 * Something is really wrong if this call fails.  Returning
169	 * error won't do because we've already obtained the read
170	 * lock.  Decrementing 'state' is no good because we probably
171	 * don't have the monitor lock.
172	 */
173	pthread_mutex_unlock(&prwlock->lock);
174
175	return(ret);
176}
177
178int
179pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
180{
181	pthread_rwlock_t 	prwlock;
182	int			ret = 0;
183
184	if (rwlock == NULL)
185		return(EINVAL);
186
187	prwlock = *rwlock;
188
189	/* check for static initialization */
190	if (prwlock == NULL) {
191		if ((ret = init_static(rwlock)) != 0)
192			return(ret);
193
194		prwlock = *rwlock;
195	}
196
197	/* grab the monitor lock */
198	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
199		return(ret);
200
201	/* give writers priority over readers */
202	if (prwlock->blocked_writers || prwlock->state < 0)
203		ret = EWOULDBLOCK;
204	else if (prwlock->state == MAX_READ_LOCKS)
205		ret = EAGAIN; /* too many read locks acquired */
206	else
207		++prwlock->state; /* indicate we are locked for reading */
208
209	/* see the comment on this in pthread_rwlock_rdlock */
210	pthread_mutex_unlock(&prwlock->lock);
211
212	return(ret);
213}
214
215int
216pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
217{
218	pthread_rwlock_t 	prwlock;
219	int			ret = 0;
220
221	if (rwlock == NULL)
222		return(EINVAL);
223
224	prwlock = *rwlock;
225
226	/* check for static initialization */
227	if (prwlock == NULL) {
228		if ((ret = init_static(rwlock)) != 0)
229			return(ret);
230
231		prwlock = *rwlock;
232	}
233
234	/* grab the monitor lock */
235	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
236		return(ret);
237
238	if (prwlock->state != 0)
239		ret = EWOULDBLOCK;
240	else
241		/* indicate we are locked for writing */
242		prwlock->state = -1;
243
244	/* see the comment on this in pthread_rwlock_rdlock */
245	pthread_mutex_unlock(&prwlock->lock);
246
247	return(ret);
248}
249
250int
251pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
252{
253	pthread_rwlock_t 	prwlock;
254	int			ret = 0;
255
256	if (rwlock == NULL)
257		return(EINVAL);
258
259	prwlock = *rwlock;
260
261	if (prwlock == NULL)
262		return(EINVAL);
263
264	/* grab the monitor lock */
265	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
266		return(ret);
267
268	if (prwlock->state > 0) {
269		if (--prwlock->state == 0 && prwlock->blocked_writers)
270			ret = pthread_cond_signal(&prwlock->write_signal);
271	} else if (prwlock->state < 0) {
272		prwlock->state = 0;
273
274		if (prwlock->blocked_writers)
275			ret = pthread_cond_signal(&prwlock->write_signal);
276		else
277			ret = pthread_cond_broadcast(&prwlock->read_signal);
278	} else
279		ret = EINVAL;
280
281	/* see the comment on this in pthread_rwlock_rdlock */
282	pthread_mutex_unlock(&prwlock->lock);
283
284	return(ret);
285}
286
287int
288pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
289{
290	pthread_rwlock_t 	prwlock;
291	int			ret = 0;
292
293	if (rwlock == NULL)
294		return(EINVAL);
295
296	prwlock = *rwlock;
297
298	/* check for static initialization */
299	if (prwlock == NULL) {
300		if ((ret = init_static(rwlock)) != 0)
301			return(ret);
302
303		prwlock = *rwlock;
304	}
305
306	/* grab the monitor lock */
307	if ((ret = pthread_mutex_lock(&prwlock->lock)) != 0)
308		return(ret);
309
310	while (prwlock->state != 0) {
311		++prwlock->blocked_writers;
312
313		ret = pthread_cond_wait(&prwlock->write_signal, &prwlock->lock);
314
315		if (ret != 0) {
316			--prwlock->blocked_writers;
317			pthread_mutex_unlock(&prwlock->lock);
318			return(ret);
319		}
320
321		--prwlock->blocked_writers;
322	}
323
324	/* indicate we are locked for writing */
325	prwlock->state = -1;
326
327	/* see the comment on this in pthread_rwlock_rdlock */
328	pthread_mutex_unlock(&prwlock->lock);
329
330	return(ret);
331}
332
333#endif /* _THREAD_SAFE */
334