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