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