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 $ */
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 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
);
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
));
139 #ifdef ISC_RWLOCK_TRACE
140 print_lock(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_RWLOCK
,
141 ISC_MSG_PRELOCK
, "prelock"), rwl
, type
);
144 if (type
== isc_rwlocktype_read
) {
145 if (rwl
->readers_waiting
!= 0)
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
;
158 } else if (nonblock
) {
159 result
= ISC_R_LOCKBUSY
;
163 rwl
->readers_waiting
++;
164 WAIT(&rwl
->readable
, &rwl
->lock
);
165 rwl
->readers_waiting
--;
169 if (rwl
->writers_waiting
!= 0)
172 if (!skip
&& rwl
->active
== 0) {
173 rwl
->type
= isc_rwlocktype_write
;
177 } else if (nonblock
) {
178 result
= ISC_R_LOCKBUSY
;
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
);
200 isc_rwlock_lock(isc_rwlock_t
*rwl
, isc_rwlocktype_t type
) {
201 return (doit(rwl
, type
, ISC_FALSE
));
205 isc_rwlock_trylock(isc_rwlock_t
*rwl
, isc_rwlocktype_t type
) {
206 return (doit(rwl
, type
, ISC_TRUE
));
210 isc_rwlock_tryupgrade(isc_rwlock_t
*rwl
) {
211 isc_result_t result
= ISC_R_SUCCESS
;
213 REQUIRE(VALID_RWLOCK(rwl
));
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
;
224 result
= ISC_R_LOCKBUSY
;
231 isc_rwlock_downgrade(isc_rwlock_t
*rwl
) {
233 REQUIRE(VALID_RWLOCK(rwl
));
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
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
);
254 isc_rwlock_unlock(isc_rwlock_t
*rwl
, isc_rwlocktype_t type
) {
256 REQUIRE(VALID_RWLOCK(rwl
));
258 REQUIRE(rwl
->type
== type
);
262 #ifdef ISC_RWLOCK_TRACE
263 print_lock(isc_msgcat_get(isc_msgcat
, ISC_MSGSET_RWLOCK
,
264 ISC_MSG_PREUNLOCK
, "preunlock"), rwl
, type
);
267 INSIST(rwl
->active
> 0);
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
) {
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
);
284 if (rwl
->readers_waiting
> 0) {
285 if (rwl
->writers_waiting
> 0 &&
286 rwl
->granted
< rwl
->write_quota
) {
287 SIGNAL(&rwl
->writeable
);
290 rwl
->type
= isc_rwlocktype_read
;
291 BROADCAST(&rwl
->readable
);
293 } else if (rwl
->writers_waiting
> 0) {
295 SIGNAL(&rwl
->writeable
);
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"),
311 return (ISC_R_SUCCESS
);
315 isc_rwlock_destroy(isc_rwlock_t
*rwl
) {
316 REQUIRE(VALID_RWLOCK(rwl
));
319 REQUIRE(rwl
->active
== 0 &&
320 rwl
->readers_waiting
== 0 &&
321 rwl
->writers_waiting
== 0);
325 (void)isc_condition_destroy(&rwl
->readable
);
326 (void)isc_condition_destroy(&rwl
->writeable
);
327 DESTROYLOCK(&rwl
->lock
);
330 #else /* ISC_PLATFORM_USETHREADS */
333 isc_rwlock_init(isc_rwlock_t
*rwl
, unsigned int read_quota
,
334 unsigned int write_quota
)
336 REQUIRE(rwl
!= NULL
);
341 rwl
->type
= isc_rwlocktype_read
;
343 rwl
->magic
= RWLOCK_MAGIC
;
345 return (ISC_R_SUCCESS
);
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
;
358 if (rwl
->active
!= 0)
359 return (ISC_R_LOCKBUSY
);
360 rwl
->type
= isc_rwlocktype_write
;
363 return (ISC_R_SUCCESS
);
367 isc_rwlock_trylock(isc_rwlock_t
*rwl
, isc_rwlocktype_t type
) {
368 return (isc_rwlock_lock(rwl
, type
));
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
;
383 result
= ISC_R_LOCKBUSY
;
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
;
398 isc_rwlock_unlock(isc_rwlock_t
*rwl
, isc_rwlocktype_t type
) {
399 REQUIRE(VALID_RWLOCK(rwl
));
400 REQUIRE(rwl
->type
== type
);
404 INSIST(rwl
->active
> 0);
407 return (ISC_R_SUCCESS
);
411 isc_rwlock_destroy(isc_rwlock_t
*rwl
) {
412 REQUIRE(rwl
!= NULL
);
413 REQUIRE(rwl
->active
== 0);
417 #endif /* ISC_PLATFORM_USETHREADS */