Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / rwlock.c
blob3a39aaf49192373a28b0d1d74dde1eeee8525332
1 /*
2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1998-2001, 2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: rwlock.c,v 1.33.2.5 2004/03/09 06:11:51 marka Exp $ */
20 #include <config.h>
22 #include <stddef.h>
24 #include <isc/magic.h>
25 #include <isc/msgs.h>
26 #include <isc/platform.h>
27 #include <isc/rwlock.h>
28 #include <isc/util.h>
30 #define RWLOCK_MAGIC ISC_MAGIC('R', 'W', 'L', 'k')
31 #define VALID_RWLOCK(rwl) ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)
33 #ifdef ISC_PLATFORM_USETHREADS
35 #ifndef RWLOCK_DEFAULT_READ_QUOTA
36 #define RWLOCK_DEFAULT_READ_QUOTA 4
37 #endif
39 #ifndef RWLOCK_DEFAULT_WRITE_QUOTA
40 #define RWLOCK_DEFAULT_WRITE_QUOTA 4
41 #endif
43 #ifdef ISC_RWLOCK_TRACE
44 #include <stdio.h> /* Required for fprintf/stderr. */
45 #include <isc/thread.h> /* Requried for isc_thread_self(). */
47 static void
48 print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
49 fprintf(stderr,
50 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
51 ISC_MSG_PRINTLOCK,
52 "rwlock %p thread %lu %s(%s): %s, %u active, "
53 "%u granted, %u rwaiting, %u wwaiting\n"),
54 rwl, isc_thread_self(), operation,
55 (type == isc_rwlocktype_read ?
56 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
57 ISC_MSG_READ, "read") :
58 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
59 ISC_MSG_WRITE, "write")),
60 (rwl->type == isc_rwlocktype_read ?
61 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
62 ISC_MSG_READING, "reading") :
63 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
64 ISC_MSG_WRITING, "writing")),
65 rwl->active, rwl->granted, rwl->readers_waiting,
66 rwl->writers_waiting);
68 #endif
70 isc_result_t
71 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
72 unsigned int write_quota)
74 isc_result_t result;
76 REQUIRE(rwl != NULL);
79 * In case there's trouble initializing, we zero magic now. If all
80 * goes well, we'll set it to RWLOCK_MAGIC.
82 rwl->magic = 0;
84 rwl->type = isc_rwlocktype_read;
85 rwl->original = isc_rwlocktype_none;
86 rwl->active = 0;
87 rwl->granted = 0;
88 rwl->readers_waiting = 0;
89 rwl->writers_waiting = 0;
90 if (read_quota == 0)
91 read_quota = RWLOCK_DEFAULT_READ_QUOTA;
92 rwl->read_quota = read_quota;
93 if (write_quota == 0)
94 write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
95 rwl->write_quota = write_quota;
96 result = isc_mutex_init(&rwl->lock);
97 if (result != ISC_R_SUCCESS) {
98 UNEXPECTED_ERROR(__FILE__, __LINE__,
99 "isc_mutex_init() %s: %s",
100 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
101 ISC_MSG_FAILED, "failed"),
102 isc_result_totext(result));
103 return (ISC_R_UNEXPECTED);
105 result = isc_condition_init(&rwl->readable);
106 if (result != ISC_R_SUCCESS) {
107 UNEXPECTED_ERROR(__FILE__, __LINE__,
108 "isc_condition_init(readable) %s: %s",
109 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
110 ISC_MSG_FAILED, "failed"),
111 isc_result_totext(result));
112 return (ISC_R_UNEXPECTED);
114 result = isc_condition_init(&rwl->writeable);
115 if (result != ISC_R_SUCCESS) {
116 UNEXPECTED_ERROR(__FILE__, __LINE__,
117 "isc_condition_init(writeable) %s: %s",
118 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
119 ISC_MSG_FAILED, "failed"),
120 isc_result_totext(result));
121 return (ISC_R_UNEXPECTED);
124 rwl->magic = RWLOCK_MAGIC;
126 return (ISC_R_SUCCESS);
129 static isc_result_t
130 doit(isc_rwlock_t *rwl, isc_rwlocktype_t type, isc_boolean_t nonblock) {
131 isc_boolean_t skip = ISC_FALSE;
132 isc_boolean_t done = ISC_FALSE;
133 isc_result_t result = ISC_R_SUCCESS;
135 REQUIRE(VALID_RWLOCK(rwl));
137 LOCK(&rwl->lock);
139 #ifdef ISC_RWLOCK_TRACE
140 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
141 ISC_MSG_PRELOCK, "prelock"), rwl, type);
142 #endif
144 if (type == isc_rwlocktype_read) {
145 if (rwl->readers_waiting != 0)
146 skip = ISC_TRUE;
147 while (!done) {
148 if (!skip &&
149 ((rwl->active == 0 ||
150 (rwl->type == isc_rwlocktype_read &&
151 (rwl->writers_waiting == 0 ||
152 rwl->granted < rwl->read_quota)))))
154 rwl->type = isc_rwlocktype_read;
155 rwl->active++;
156 rwl->granted++;
157 done = ISC_TRUE;
158 } else if (nonblock) {
159 result = ISC_R_LOCKBUSY;
160 done = ISC_TRUE;
161 } else {
162 skip = ISC_FALSE;
163 rwl->readers_waiting++;
164 WAIT(&rwl->readable, &rwl->lock);
165 rwl->readers_waiting--;
168 } else {
169 if (rwl->writers_waiting != 0)
170 skip = ISC_TRUE;
171 while (!done) {
172 if (!skip && rwl->active == 0) {
173 rwl->type = isc_rwlocktype_write;
174 rwl->active = 1;
175 rwl->granted++;
176 done = ISC_TRUE;
177 } else if (nonblock) {
178 result = ISC_R_LOCKBUSY;
179 done = ISC_TRUE;
180 } else {
181 skip = ISC_FALSE;
182 rwl->writers_waiting++;
183 WAIT(&rwl->writeable, &rwl->lock);
184 rwl->writers_waiting--;
189 #ifdef ISC_RWLOCK_TRACE
190 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
191 ISC_MSG_POSTLOCK, "postlock"), rwl, type);
192 #endif
194 UNLOCK(&rwl->lock);
196 return (result);
199 isc_result_t
200 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
201 return (doit(rwl, type, ISC_FALSE));
204 isc_result_t
205 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
206 return (doit(rwl, type, ISC_TRUE));
209 isc_result_t
210 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
211 isc_result_t result = ISC_R_SUCCESS;
213 REQUIRE(VALID_RWLOCK(rwl));
214 LOCK(&rwl->lock);
215 REQUIRE(rwl->type == isc_rwlocktype_read);
216 REQUIRE(rwl->active != 0);
218 /* If we are the only reader then succeed. */
219 if (rwl->active == 1) {
220 rwl->original = (rwl->original == isc_rwlocktype_none) ?
221 isc_rwlocktype_read : isc_rwlocktype_none;
222 rwl->type = isc_rwlocktype_write;
223 } else
224 result = ISC_R_LOCKBUSY;
226 UNLOCK(&rwl->lock);
227 return (result);
230 void
231 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
233 REQUIRE(VALID_RWLOCK(rwl));
234 LOCK(&rwl->lock);
235 REQUIRE(rwl->type == isc_rwlocktype_write);
236 REQUIRE(rwl->active == 1);
238 rwl->type = isc_rwlocktype_read;
239 rwl->original = (rwl->original == isc_rwlocktype_none) ?
240 isc_rwlocktype_write : isc_rwlocktype_none;
242 * Resume processing any read request that were blocked when
243 * we upgraded.
245 if (rwl->original == isc_rwlocktype_none &&
246 (rwl->writers_waiting == 0 || rwl->granted < rwl->read_quota) &&
247 rwl->readers_waiting > 0)
248 BROADCAST(&rwl->readable);
250 UNLOCK(&rwl->lock);
253 isc_result_t
254 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
256 REQUIRE(VALID_RWLOCK(rwl));
257 LOCK(&rwl->lock);
258 REQUIRE(rwl->type == type);
260 UNUSED(type);
262 #ifdef ISC_RWLOCK_TRACE
263 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
264 ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
265 #endif
267 INSIST(rwl->active > 0);
268 rwl->active--;
269 if (rwl->active == 0) {
270 if (rwl->original != isc_rwlocktype_none) {
271 rwl->type = rwl->original;
272 rwl->original = isc_rwlocktype_none;
274 if (rwl->type == isc_rwlocktype_read) {
275 rwl->granted = 0;
276 if (rwl->writers_waiting > 0) {
277 rwl->type = isc_rwlocktype_write;
278 SIGNAL(&rwl->writeable);
279 } else if (rwl->readers_waiting > 0) {
280 /* Does this case ever happen? */
281 BROADCAST(&rwl->readable);
283 } else {
284 if (rwl->readers_waiting > 0) {
285 if (rwl->writers_waiting > 0 &&
286 rwl->granted < rwl->write_quota) {
287 SIGNAL(&rwl->writeable);
288 } else {
289 rwl->granted = 0;
290 rwl->type = isc_rwlocktype_read;
291 BROADCAST(&rwl->readable);
293 } else if (rwl->writers_waiting > 0) {
294 rwl->granted = 0;
295 SIGNAL(&rwl->writeable);
296 } else {
297 rwl->granted = 0;
301 INSIST(rwl->original == isc_rwlocktype_none);
303 #ifdef ISC_RWLOCK_TRACE
304 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
305 ISC_MSG_POSTUNLOCK, "postunlock"),
306 rwl, type);
307 #endif
309 UNLOCK(&rwl->lock);
311 return (ISC_R_SUCCESS);
314 void
315 isc_rwlock_destroy(isc_rwlock_t *rwl) {
316 REQUIRE(VALID_RWLOCK(rwl));
318 LOCK(&rwl->lock);
319 REQUIRE(rwl->active == 0 &&
320 rwl->readers_waiting == 0 &&
321 rwl->writers_waiting == 0);
322 UNLOCK(&rwl->lock);
324 rwl->magic = 0;
325 (void)isc_condition_destroy(&rwl->readable);
326 (void)isc_condition_destroy(&rwl->writeable);
327 DESTROYLOCK(&rwl->lock);
330 #else /* ISC_PLATFORM_USETHREADS */
332 isc_result_t
333 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
334 unsigned int write_quota)
336 REQUIRE(rwl != NULL);
338 UNUSED(read_quota);
339 UNUSED(write_quota);
341 rwl->type = isc_rwlocktype_read;
342 rwl->active = 0;
343 rwl->magic = RWLOCK_MAGIC;
345 return (ISC_R_SUCCESS);
348 isc_result_t
349 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
350 REQUIRE(VALID_RWLOCK(rwl));
352 if (type == isc_rwlocktype_read) {
353 if (rwl->type != isc_rwlocktype_read && rwl->active != 0)
354 return (ISC_R_LOCKBUSY);
355 rwl->type = isc_rwlocktype_read;
356 rwl->active++;
357 } else {
358 if (rwl->active != 0)
359 return (ISC_R_LOCKBUSY);
360 rwl->type = isc_rwlocktype_write;
361 rwl->active = 1;
363 return (ISC_R_SUCCESS);
366 isc_result_t
367 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
368 return (isc_rwlock_lock(rwl, type));
371 isc_result_t
372 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
373 isc_result_t result = ISC_R_SUCCESS;
375 REQUIRE(VALID_RWLOCK(rwl));
376 REQUIRE(rwl->type == isc_rwlocktype_read);
377 REQUIRE(rwl->active != 0);
379 /* If we are the only reader then succeed. */
380 if (rwl->active == 1)
381 rwl->type = isc_rwlocktype_write;
382 else
383 result = ISC_R_LOCKBUSY;
384 return (result);
387 void
388 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
390 REQUIRE(VALID_RWLOCK(rwl));
391 REQUIRE(rwl->type == isc_rwlocktype_write);
392 REQUIRE(rwl->active == 1);
394 rwl->type = isc_rwlocktype_read;
397 isc_result_t
398 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
399 REQUIRE(VALID_RWLOCK(rwl));
400 REQUIRE(rwl->type == type);
402 UNUSED(type);
404 INSIST(rwl->active > 0);
405 rwl->active--;
407 return (ISC_R_SUCCESS);
410 void
411 isc_rwlock_destroy(isc_rwlock_t *rwl) {
412 REQUIRE(rwl != NULL);
413 REQUIRE(rwl->active == 0);
414 rwl->magic = 0;
417 #endif /* ISC_PLATFORM_USETHREADS */