thr_rwlock.c revision 172491
1155324Simp/*-
2155324Simp * Copyright (c) 1998 Alex Nash
3155324Simp * All rights reserved.
4155324Simp *
5155324Simp * Redistribution and use in source and binary forms, with or without
6155324Simp * modification, are permitted provided that the following conditions
7155324Simp * are met:
8155324Simp * 1. Redistributions of source code must retain the above copyright
9155324Simp *    notice, this list of conditions and the following disclaimer.
10155324Simp * 2. Redistributions in binary form must reproduce the above copyright
11155324Simp *    notice, this list of conditions and the following disclaimer in the
12155324Simp *    documentation and/or other materials provided with the distribution.
13155324Simp *
14155324Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15155324Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16155324Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17155324Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18155324Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19155324Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20155324Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21155324Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22155324Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23155324Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24155324Simp * SUCH DAMAGE.
25155324Simp *
26155324Simp * $FreeBSD: head/lib/libkse/thread/thr_rwlock.c 156611 2006-03-13 00:59:51Z deischen $
27155324Simp */
28155324Simp
29155324Simp#include <errno.h>
30155324Simp#include <limits.h>
31155324Simp#include <stdlib.h>
32155324Simp
33155324Simp#include "namespace.h"
34155324Simp#include <pthread.h>
35155324Simp#include "un-namespace.h"
36155324Simp#include "thr_private.h"
37155324Simp
38155324Simp/* maximum number of times a read lock may be obtained */
39155324Simp#define	MAX_READ_LOCKS		(INT_MAX - 1)
40155324Simp
41155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_destroy);
42155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_destroy);
43155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_init);
44155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_init);
45155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_rdlock);
46155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_rdlock);
47155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_timedrdlock);
48155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_timedrdlock);
49155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_tryrdlock);
50155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_tryrdlock);
51155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_trywrlock);
52155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_trywrlock);
53155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_unlock);
54155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_unlock);
55155324SimpLT10_COMPAT_PRIVATE(_pthread_rwlock_wrlock);
56155324SimpLT10_COMPAT_DEFAULT(pthread_rwlock_wrlock);
57161704ScognetLT10_COMPAT_PRIVATE(_pthread_rwlock_timedwrlock);
58161704ScognetLT10_COMPAT_DEFAULT(pthread_rwlock_timedwrlock);
59155324Simp
60161704Scognet__weak_reference(_pthread_rwlock_destroy, pthread_rwlock_destroy);
61161704Scognet__weak_reference(_pthread_rwlock_init, pthread_rwlock_init);
62161704Scognet__weak_reference(_pthread_rwlock_rdlock, pthread_rwlock_rdlock);
63155324Simp__weak_reference(_pthread_rwlock_timedrdlock, pthread_rwlock_timedrdlock);
64155324Simp__weak_reference(_pthread_rwlock_tryrdlock, pthread_rwlock_tryrdlock);
65155324Simp__weak_reference(_pthread_rwlock_trywrlock, pthread_rwlock_trywrlock);
66155324Simp__weak_reference(_pthread_rwlock_unlock, pthread_rwlock_unlock);
67155324Simp__weak_reference(_pthread_rwlock_wrlock, pthread_rwlock_wrlock);
68155324Simp__weak_reference(_pthread_rwlock_timedwrlock, pthread_rwlock_timedwrlock);
69155324Simp
70155324Simp/*
71155324Simp * Prototypes
72155324Simp */
73155324Simpstatic int init_static(pthread_rwlock_t *rwlock);
74155324Simp
75155324Simp
76155324Simpstatic int
77155324Simpinit_static(pthread_rwlock_t *rwlock)
78155324Simp{
79155324Simp	struct pthread *thread = _get_curthread();
80155324Simp	int ret;
81155324Simp
82155324Simp	THR_LOCK_ACQUIRE(thread, &_rwlock_static_lock);
83155324Simp
84155324Simp	if (*rwlock == NULL)
85155324Simp		ret = _pthread_rwlock_init(rwlock, NULL);
86155324Simp	else
87155324Simp		ret = 0;
88155324Simp
89155324Simp	THR_LOCK_RELEASE(thread, &_rwlock_static_lock);
90155324Simp	return (ret);
91156828Simp}
92156828Simp
93156828Simpint
94156828Simp_pthread_rwlock_destroy (pthread_rwlock_t *rwlock)
95156828Simp{
96156828Simp	int ret;
97155324Simp
98155324Simp	if (rwlock == NULL)
99155324Simp		ret = EINVAL;
100155324Simp	else {
101155324Simp		pthread_rwlock_t prwlock;
102155324Simp
103155324Simp		prwlock = *rwlock;
104155324Simp
105155324Simp		_pthread_mutex_destroy(&prwlock->lock);
106155324Simp		_pthread_cond_destroy(&prwlock->read_signal);
107155324Simp		_pthread_cond_destroy(&prwlock->write_signal);
108155324Simp		free(prwlock);
109155324Simp
110155324Simp		*rwlock = NULL;
111155324Simp
112155324Simp		ret = 0;
113155324Simp	}
114156828Simp	return (ret);
115155324Simp}
116155324Simp
117155324Simpint
118155324Simp_pthread_rwlock_init (pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr)
119155324Simp{
120155324Simp	pthread_rwlock_t prwlock;
121155324Simp	int ret;
122155324Simp
123155324Simp	/* allocate rwlock object */
124155324Simp	prwlock = (pthread_rwlock_t)malloc(sizeof(struct pthread_rwlock));
125155324Simp
126155324Simp	if (prwlock == NULL)
127155324Simp		return (ENOMEM);
128155324Simp
129158746Scognet	/* initialize the lock */
130155324Simp	if ((ret = _pthread_mutex_init(&prwlock->lock, NULL)) != 0)
131155324Simp		free(prwlock);
132155324Simp	else {
133155324Simp		/* initialize the read condition signal */
134155324Simp		ret = _pthread_cond_init(&prwlock->read_signal, NULL);
135155324Simp
136155324Simp		if (ret != 0) {
137155324Simp			_pthread_mutex_destroy(&prwlock->lock);
138155324Simp			free(prwlock);
139155324Simp		} else {
140155324Simp			/* initialize the write condition signal */
141155324Simp			ret = _pthread_cond_init(&prwlock->write_signal, NULL);
142155324Simp
143155324Simp			if (ret != 0) {
144155324Simp				_pthread_cond_destroy(&prwlock->read_signal);
145155324Simp				_pthread_mutex_destroy(&prwlock->lock);
146155324Simp				free(prwlock);
147155324Simp			} else {
148155324Simp				/* success */
149155324Simp				prwlock->state = 0;
150155324Simp				prwlock->blocked_writers = 0;
151155324Simp
152155324Simp				*rwlock = prwlock;
153155324Simp			}
154155324Simp		}
155155324Simp	}
156155324Simp
157155324Simp	return (ret);
158155324Simp}
159155324Simp
160155324Simpstatic int
161155324Simprwlock_rdlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
162155324Simp{
163155324Simp	pthread_rwlock_t prwlock;
164155324Simp	struct pthread *curthread;
165155324Simp	int ret;
166155324Simp
167155324Simp	if (rwlock == NULL)
168155324Simp		return (EINVAL);
169155324Simp
170155324Simp	prwlock = *rwlock;
171155324Simp
172155324Simp	/* check for static initialization */
173155324Simp	if (prwlock == NULL) {
174155324Simp		if ((ret = init_static(rwlock)) != 0)
175155324Simp			return (ret);
176155324Simp
177155324Simp		prwlock = *rwlock;
178155324Simp	}
179155324Simp
180155324Simp	/* grab the monitor lock */
181155324Simp	if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
182155324Simp		return (ret);
183155324Simp
184155324Simp	/* check lock count */
185155324Simp	if (prwlock->state == MAX_READ_LOCKS) {
186155324Simp		_thr_mutex_unlock(&prwlock->lock);
187155324Simp		return (EAGAIN);
188155324Simp	}
189155324Simp
190155324Simp	curthread = _get_curthread();
191155324Simp	if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
192155324Simp		/*
193155324Simp		 * To avoid having to track all the rdlocks held by
194155324Simp		 * a thread or all of the threads that hold a rdlock,
195155324Simp		 * we keep a simple count of all the rdlocks held by
196155324Simp		 * a thread.  If a thread holds any rdlocks it is
197155324Simp		 * possible that it is attempting to take a recursive
198155324Simp		 * rdlock.  If there are blocked writers and precedence
199155324Simp		 * is given to them, then that would result in the thread
200155324Simp		 * deadlocking.  So allowing a thread to take the rdlock
201155324Simp		 * when it already has one or more rdlocks avoids the
202156828Simp		 * deadlock.  I hope the reader can follow that logic ;-)
203155324Simp		 */
204155324Simp		;	/* nothing needed */
205155324Simp	} else {
206155324Simp		/* give writers priority over readers */
207155324Simp		while (prwlock->blocked_writers || prwlock->state < 0) {
208157564Simp			if (abstime)
209157564Simp				ret = _pthread_cond_timedwait
210157564Simp				    (&prwlock->read_signal,
211157564Simp				    &prwlock->lock, abstime);
212155324Simp			else
213155324Simp				ret = _thr_cond_wait(&prwlock->read_signal,
214155324Simp			    &prwlock->lock);
215157564Simp			if (ret != 0) {
216155324Simp				/* can't do a whole lot if this fails */
217155324Simp				_thr_mutex_unlock(&prwlock->lock);
218155324Simp				return (ret);
219155324Simp			}
220156828Simp		}
221156828Simp	}
222156828Simp
223156828Simp	curthread->rdlock_count++;
224156828Simp	prwlock->state++; /* indicate we are locked for reading */
225156828Simp
226155324Simp	/*
227155324Simp	 * Something is really wrong if this call fails.  Returning
228155324Simp	 * error won't do because we've already obtained the read
229155324Simp	 * lock.  Decrementing 'state' is no good because we probably
230156828Simp	 * don't have the monitor lock.
231156828Simp	 */
232156828Simp	_thr_mutex_unlock(&prwlock->lock);
233156828Simp
234156828Simp	return (ret);
235156828Simp}
236156828Simp
237156828Simpint
238156828Simp_pthread_rwlock_rdlock (pthread_rwlock_t *rwlock)
239156828Simp{
240155324Simp	return (rwlock_rdlock_common(rwlock, NULL));
241156828Simp}
242156828Simp
243156828Simp__strong_reference(_pthread_rwlock_rdlock, _thr_rwlock_rdlock);
244156828Simp
245156828Simpint
246156828Simp_pthread_rwlock_timedrdlock (pthread_rwlock_t *rwlock,
247156828Simp	 const struct timespec *abstime)
248156828Simp{
249156828Simp	return (rwlock_rdlock_common(rwlock, abstime));
250156828Simp}
251156828Simp
252156828Simpint
253156828Simp_pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock)
254156828Simp{
255156828Simp	struct pthread *curthread;
256156828Simp	pthread_rwlock_t prwlock;
257156828Simp	int ret;
258156828Simp
259156828Simp	if (rwlock == NULL)
260156828Simp		return (EINVAL);
261156828Simp
262156828Simp	prwlock = *rwlock;
263156828Simp
264156828Simp	/* check for static initialization */
265156828Simp	if (prwlock == NULL) {
266156828Simp		if ((ret = init_static(rwlock)) != 0)
267156828Simp			return (ret);
268156828Simp
269156828Simp		prwlock = *rwlock;
270156828Simp	}
271156828Simp
272156828Simp	/* grab the monitor lock */
273156828Simp	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
274156828Simp		return (ret);
275156828Simp
276156828Simp	curthread = _get_curthread();
277156828Simp	if (prwlock->state == MAX_READ_LOCKS)
278156828Simp		ret = EAGAIN;
279156828Simp	else if ((curthread->rdlock_count > 0) && (prwlock->state > 0)) {
280156828Simp		/* see comment for pthread_rwlock_rdlock() */
281156828Simp		curthread->rdlock_count++;
282156828Simp		prwlock->state++;
283156828Simp	}
284156828Simp	/* give writers priority over readers */
285156828Simp	else if (prwlock->blocked_writers || prwlock->state < 0)
286156828Simp		ret = EBUSY;
287156828Simp	else {
288156828Simp		curthread->rdlock_count++;
289156828Simp		prwlock->state++; /* indicate we are locked for reading */
290156828Simp	}
291156828Simp
292156828Simp	/* see the comment on this in pthread_rwlock_rdlock */
293156828Simp	_pthread_mutex_unlock(&prwlock->lock);
294156828Simp
295156828Simp	return (ret);
296156828Simp}
297156828Simp
298156828Simpint
299156828Simp_pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock)
300156828Simp{
301156828Simp	pthread_rwlock_t prwlock;
302156828Simp	int ret;
303156828Simp
304156828Simp	if (rwlock == NULL)
305156828Simp		return (EINVAL);
306156828Simp
307156828Simp	prwlock = *rwlock;
308156828Simp
309156828Simp	/* check for static initialization */
310156828Simp	if (prwlock == NULL) {
311156828Simp		if ((ret = init_static(rwlock)) != 0)
312156828Simp			return (ret);
313156828Simp
314156828Simp		prwlock = *rwlock;
315156828Simp	}
316156828Simp
317156828Simp	/* grab the monitor lock */
318156828Simp	if ((ret = _pthread_mutex_lock(&prwlock->lock)) != 0)
319156828Simp		return (ret);
320156828Simp
321158531Scognet	if (prwlock->state != 0)
322156828Simp		ret = EBUSY;
323156828Simp	else
324156828Simp		/* indicate we are locked for writing */
325156828Simp		prwlock->state = -1;
326156828Simp
327156828Simp	/* see the comment on this in pthread_rwlock_rdlock */
328156828Simp	_pthread_mutex_unlock(&prwlock->lock);
329156828Simp
330156828Simp	return (ret);
331156828Simp}
332156828Simp
333156828Simpint
334156828Simp_pthread_rwlock_unlock (pthread_rwlock_t *rwlock)
335156828Simp{
336156828Simp	struct pthread *curthread;
337156828Simp	pthread_rwlock_t prwlock;
338156828Simp	int ret;
339156828Simp
340156828Simp	if (rwlock == NULL)
341156828Simp		return (EINVAL);
342156828Simp
343156828Simp	prwlock = *rwlock;
344156828Simp
345156828Simp	if (prwlock == NULL)
346156828Simp		return (EINVAL);
347158531Scognet
348156828Simp	/* grab the monitor lock */
349158531Scognet	if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
350158531Scognet		return (ret);
351158531Scognet
352158531Scognet	curthread = _get_curthread();
353158531Scognet	if (prwlock->state > 0) {
354158531Scognet		curthread->rdlock_count--;
355156828Simp		prwlock->state--;
356156828Simp		if (prwlock->state == 0 && prwlock->blocked_writers)
357156828Simp			ret = _thr_cond_signal(&prwlock->write_signal);
358156828Simp	} else if (prwlock->state < 0) {
359156828Simp		prwlock->state = 0;
360156828Simp
361156828Simp		if (prwlock->blocked_writers)
362156828Simp			ret = _thr_cond_signal(&prwlock->write_signal);
363156828Simp		else
364156828Simp			ret = _thr_cond_broadcast(&prwlock->read_signal);
365156828Simp	} else
366156828Simp		ret = EINVAL;
367156828Simp
368156828Simp	/* see the comment on this in pthread_rwlock_rdlock */
369156828Simp	_thr_mutex_unlock(&prwlock->lock);
370156828Simp
371156828Simp	return (ret);
372156828Simp}
373156828Simp
374156828Simp__strong_reference(_pthread_rwlock_unlock, _thr_rwlock_unlock);
375156828Simp
376156828Simpstatic int
377156828Simprwlock_wrlock_common (pthread_rwlock_t *rwlock, const struct timespec *abstime)
378156828Simp{
379156828Simp	pthread_rwlock_t prwlock;
380156828Simp	int ret;
381156828Simp
382156828Simp	if (rwlock == NULL)
383156828Simp		return (EINVAL);
384156828Simp
385156828Simp	prwlock = *rwlock;
386156828Simp
387156828Simp	/* check for static initialization */
388156828Simp	if (prwlock == NULL) {
389156828Simp		if ((ret = init_static(rwlock)) != 0)
390156828Simp			return (ret);
391156828Simp
392156828Simp		prwlock = *rwlock;
393156828Simp	}
394156828Simp
395156828Simp	/* grab the monitor lock */
396156828Simp	if ((ret = _thr_mutex_lock(&prwlock->lock)) != 0)
397156828Simp		return (ret);
398156828Simp
399155324Simp	while (prwlock->state != 0) {
400155324Simp		prwlock->blocked_writers++;
401155324Simp
402155324Simp		if (abstime != NULL)
403155324Simp			ret = _pthread_cond_timedwait(&prwlock->write_signal,
404155324Simp			    &prwlock->lock, abstime);
405155324Simp		else
406155324Simp			ret = _thr_cond_wait(&prwlock->write_signal,
407155324Simp			    &prwlock->lock);
408155324Simp		if (ret != 0) {
409155324Simp			prwlock->blocked_writers--;
410155324Simp			_thr_mutex_unlock(&prwlock->lock);
411155324Simp			return (ret);
412155324Simp		}
413155324Simp
414155324Simp		prwlock->blocked_writers--;
415155324Simp	}
416156828Simp
417156828Simp	/* indicate we are locked for writing */
418156828Simp	prwlock->state = -1;
419156828Simp
420155324Simp	/* see the comment on this in pthread_rwlock_rdlock */
421155324Simp	_thr_mutex_unlock(&prwlock->lock);
422155324Simp
423155324Simp	return (ret);
424161704Scognet}
425161704Scognet
426155324Simpint
427156828Simp_pthread_rwlock_wrlock (pthread_rwlock_t *rwlock)
428156828Simp{
429156828Simp	return (rwlock_wrlock_common (rwlock, NULL));
430155324Simp}
431155324Simp__strong_reference(_pthread_rwlock_wrlock, _thr_rwlock_wrlock);
432155324Simp
433155324Simpint
434155324Simp_pthread_rwlock_timedwrlock (pthread_rwlock_t *rwlock,
435155324Simp    const struct timespec *abstime)
436155324Simp{
437155324Simp	return (rwlock_wrlock_common (rwlock, abstime));
438155324Simp}
439155324Simp