2 * Copyright (C) 2004, 2005 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.4.2.3 2005/03/17 03:58:32 marka Exp $ */
24 #include <isc/magic.h>
26 #include <isc/platform.h>
27 #include <isc/rwlock.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
39 #ifndef RWLOCK_DEFAULT_WRITE_QUOTA
40 #define RWLOCK_DEFAULT_WRITE_QUOTA 4
43 #ifdef ISC_RWLOCK_TRACE
44 #include <stdio.h> /* Required for fprintf/stderr. */
45 #include <isc/thread.h> /* Requried for isc_thread_self(). */
48 print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
50 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
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);
71 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
72 unsigned int write_quota)
79 * In case there's trouble initializing, we zero magic now. If all
80 * goes well, we'll set it to RWLOCK_MAGIC.
84 rwl->type = isc_rwlocktype_read;
85 rwl->original = isc_rwlocktype_none;
88 rwl->readers_waiting = 0;
89 rwl->writers_waiting = 0;
91 read_quota = RWLOCK_DEFAULT_READ_QUOTA;
92 rwl->read_quota = read_quota;
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 result = ISC_R_UNEXPECTED;
116 result = isc_condition_init(&rwl->writeable);
117 if (result != ISC_R_SUCCESS) {
118 UNEXPECTED_ERROR(__FILE__, __LINE__,
119 "isc_condition_init(writeable) %s: %s",
120 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
121 ISC_MSG_FAILED, "failed"),
122 isc_result_totext(result));
123 result = ISC_R_UNEXPECTED;
127 rwl->magic = RWLOCK_MAGIC;
129 return (ISC_R_SUCCESS);
132 (void)isc_condition_destroy(&rwl->readable);
134 DESTROYLOCK(&rwl->lock);
140 doit(isc_rwlock_t *rwl, isc_rwlocktype_t type, isc_boolean_t nonblock) {
141 isc_boolean_t skip = ISC_FALSE;
142 isc_boolean_t done = ISC_FALSE;
143 isc_result_t result = ISC_R_SUCCESS;
145 REQUIRE(VALID_RWLOCK(rwl));
149 #ifdef ISC_RWLOCK_TRACE
150 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
151 ISC_MSG_PRELOCK, "prelock"), rwl, type);
154 if (type == isc_rwlocktype_read) {
155 if (rwl->readers_waiting != 0)
159 ((rwl->active == 0 ||
160 (rwl->type == isc_rwlocktype_read &&
161 (rwl->writers_waiting == 0 ||
162 rwl->granted < rwl->read_quota)))))
164 rwl->type = isc_rwlocktype_read;
168 } else if (nonblock) {
169 result = ISC_R_LOCKBUSY;
173 rwl->readers_waiting++;
174 WAIT(&rwl->readable, &rwl->lock);
175 rwl->readers_waiting--;
179 if (rwl->writers_waiting != 0)
182 if (!skip && rwl->active == 0) {
183 rwl->type = isc_rwlocktype_write;
187 } else if (nonblock) {
188 result = ISC_R_LOCKBUSY;
192 rwl->writers_waiting++;
193 WAIT(&rwl->writeable, &rwl->lock);
194 rwl->writers_waiting--;
199 #ifdef ISC_RWLOCK_TRACE
200 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
201 ISC_MSG_POSTLOCK, "postlock"), rwl, type);
210 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
211 return (doit(rwl, type, ISC_FALSE));
215 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
216 return (doit(rwl, type, ISC_TRUE));
220 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
221 isc_result_t result = ISC_R_SUCCESS;
223 REQUIRE(VALID_RWLOCK(rwl));
225 REQUIRE(rwl->type == isc_rwlocktype_read);
226 REQUIRE(rwl->active != 0);
228 /* If we are the only reader then succeed. */
229 if (rwl->active == 1) {
230 rwl->original = (rwl->original == isc_rwlocktype_none) ?
231 isc_rwlocktype_read : isc_rwlocktype_none;
232 rwl->type = isc_rwlocktype_write;
234 result = ISC_R_LOCKBUSY;
241 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
243 REQUIRE(VALID_RWLOCK(rwl));
245 REQUIRE(rwl->type == isc_rwlocktype_write);
246 REQUIRE(rwl->active == 1);
248 rwl->type = isc_rwlocktype_read;
249 rwl->original = (rwl->original == isc_rwlocktype_none) ?
250 isc_rwlocktype_write : isc_rwlocktype_none;
252 * Resume processing any read request that were blocked when
255 if (rwl->original == isc_rwlocktype_none &&
256 (rwl->writers_waiting == 0 || rwl->granted < rwl->read_quota) &&
257 rwl->readers_waiting > 0)
258 BROADCAST(&rwl->readable);
264 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
266 REQUIRE(VALID_RWLOCK(rwl));
268 REQUIRE(rwl->type == type);
272 #ifdef ISC_RWLOCK_TRACE
273 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
274 ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
277 INSIST(rwl->active > 0);
279 if (rwl->active == 0) {
280 if (rwl->original != isc_rwlocktype_none) {
281 rwl->type = rwl->original;
282 rwl->original = isc_rwlocktype_none;
284 if (rwl->type == isc_rwlocktype_read) {
286 if (rwl->writers_waiting > 0) {
287 rwl->type = isc_rwlocktype_write;
288 SIGNAL(&rwl->writeable);
289 } else if (rwl->readers_waiting > 0) {
290 /* Does this case ever happen? */
291 BROADCAST(&rwl->readable);
294 if (rwl->readers_waiting > 0) {
295 if (rwl->writers_waiting > 0 &&
296 rwl->granted < rwl->write_quota) {
297 SIGNAL(&rwl->writeable);
300 rwl->type = isc_rwlocktype_read;
301 BROADCAST(&rwl->readable);
303 } else if (rwl->writers_waiting > 0) {
305 SIGNAL(&rwl->writeable);
311 INSIST(rwl->original == isc_rwlocktype_none);
313 #ifdef ISC_RWLOCK_TRACE
314 print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
315 ISC_MSG_POSTUNLOCK, "postunlock"),
321 return (ISC_R_SUCCESS);
325 isc_rwlock_destroy(isc_rwlock_t *rwl) {
326 REQUIRE(VALID_RWLOCK(rwl));
329 REQUIRE(rwl->active == 0 &&
330 rwl->readers_waiting == 0 &&
331 rwl->writers_waiting == 0);
335 (void)isc_condition_destroy(&rwl->readable);
336 (void)isc_condition_destroy(&rwl->writeable);
337 DESTROYLOCK(&rwl->lock);
340 #else /* ISC_PLATFORM_USETHREADS */
343 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
344 unsigned int write_quota)
346 REQUIRE(rwl != NULL);
351 rwl->type = isc_rwlocktype_read;
353 rwl->magic = RWLOCK_MAGIC;
355 return (ISC_R_SUCCESS);
359 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
360 REQUIRE(VALID_RWLOCK(rwl));
362 if (type == isc_rwlocktype_read) {
363 if (rwl->type != isc_rwlocktype_read && rwl->active != 0)
364 return (ISC_R_LOCKBUSY);
365 rwl->type = isc_rwlocktype_read;
368 if (rwl->active != 0)
369 return (ISC_R_LOCKBUSY);
370 rwl->type = isc_rwlocktype_write;
373 return (ISC_R_SUCCESS);
377 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
378 return (isc_rwlock_lock(rwl, type));
382 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
383 isc_result_t result = ISC_R_SUCCESS;
385 REQUIRE(VALID_RWLOCK(rwl));
386 REQUIRE(rwl->type == isc_rwlocktype_read);
387 REQUIRE(rwl->active != 0);
389 /* If we are the only reader then succeed. */
390 if (rwl->active == 1)
391 rwl->type = isc_rwlocktype_write;
393 result = ISC_R_LOCKBUSY;
398 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
400 REQUIRE(VALID_RWLOCK(rwl));
401 REQUIRE(rwl->type == isc_rwlocktype_write);
402 REQUIRE(rwl->active == 1);
404 rwl->type = isc_rwlocktype_read;
408 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
409 REQUIRE(VALID_RWLOCK(rwl));
410 REQUIRE(rwl->type == type);
414 INSIST(rwl->active > 0);
417 return (ISC_R_SUCCESS);
421 isc_rwlock_destroy(isc_rwlock_t *rwl) {
422 REQUIRE(rwl != NULL);
423 REQUIRE(rwl->active == 0);
427 #endif /* ISC_PLATFORM_USETHREADS */