xref: /freebsd/lib/libthr/thread/thr_rwlock.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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$
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 
47 static int init_static (pthread_rwlock_t *rwlock);
48 
49 static spinlock_t static_init_lock = _SPINLOCK_INITIALIZER;
50 
51 static int
52 init_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 
68 int
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 
93 int
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 
136 int
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 
187 int
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 
224 int
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 
259 int
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 
296 int
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