Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / bind-9.3 / lib / isc / rwlock.c
1 /*
2  * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 1998-2001, 2003  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: rwlock.c,v 1.33.2.4.2.3 2005/03/17 03:58:32 marka Exp $ */
19
20 #include <config.h>
21
22 #include <stddef.h>
23
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>
29
30 #define RWLOCK_MAGIC            ISC_MAGIC('R', 'W', 'L', 'k')
31 #define VALID_RWLOCK(rwl)       ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)
32
33 #ifdef ISC_PLATFORM_USETHREADS
34
35 #ifndef RWLOCK_DEFAULT_READ_QUOTA
36 #define RWLOCK_DEFAULT_READ_QUOTA 4
37 #endif
38
39 #ifndef RWLOCK_DEFAULT_WRITE_QUOTA
40 #define RWLOCK_DEFAULT_WRITE_QUOTA 4
41 #endif
42
43 #ifdef ISC_RWLOCK_TRACE
44 #include <stdio.h>              /* Required for fprintf/stderr. */
45 #include <isc/thread.h>         /* Requried for isc_thread_self(). */
46
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);
67 }
68 #endif
69
70 isc_result_t
71 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
72                 unsigned int write_quota)
73 {
74         isc_result_t result;
75
76         REQUIRE(rwl != NULL);
77
78         /*
79          * In case there's trouble initializing, we zero magic now.  If all
80          * goes well, we'll set it to RWLOCK_MAGIC.
81          */
82         rwl->magic = 0;
83
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);
104         }
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;
113                 goto destroy_lock;
114
115         }
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;
124                 goto destroy_rcond;
125         }
126
127         rwl->magic = RWLOCK_MAGIC;
128
129         return (ISC_R_SUCCESS);
130
131   destroy_rcond:
132         (void)isc_condition_destroy(&rwl->readable);
133   destroy_lock:
134         DESTROYLOCK(&rwl->lock);
135
136         return (result);
137 }
138
139 static isc_result_t
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;
144
145         REQUIRE(VALID_RWLOCK(rwl));
146
147         LOCK(&rwl->lock);
148
149 #ifdef ISC_RWLOCK_TRACE
150         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
151                                   ISC_MSG_PRELOCK, "prelock"), rwl, type);
152 #endif
153
154         if (type == isc_rwlocktype_read) {
155                 if (rwl->readers_waiting != 0)
156                         skip = ISC_TRUE;
157                 while (!done) {
158                         if (!skip &&
159                             ((rwl->active == 0 ||
160                               (rwl->type == isc_rwlocktype_read &&
161                                (rwl->writers_waiting == 0 ||
162                                 rwl->granted < rwl->read_quota)))))
163                         {
164                                 rwl->type = isc_rwlocktype_read;
165                                 rwl->active++;
166                                 rwl->granted++;
167                                 done = ISC_TRUE;
168                         } else if (nonblock) {
169                                 result = ISC_R_LOCKBUSY;
170                                 done = ISC_TRUE;
171                         } else {
172                                 skip = ISC_FALSE;
173                                 rwl->readers_waiting++;
174                                 WAIT(&rwl->readable, &rwl->lock);
175                                 rwl->readers_waiting--;
176                         }
177                 }
178         } else {
179                 if (rwl->writers_waiting != 0)
180                         skip = ISC_TRUE;
181                 while (!done) {
182                         if (!skip && rwl->active == 0) {
183                                 rwl->type = isc_rwlocktype_write;
184                                 rwl->active = 1;
185                                 rwl->granted++;
186                                 done = ISC_TRUE;
187                         } else if (nonblock) {
188                                 result = ISC_R_LOCKBUSY;
189                                 done = ISC_TRUE;
190                         } else {
191                                 skip = ISC_FALSE;
192                                 rwl->writers_waiting++;
193                                 WAIT(&rwl->writeable, &rwl->lock);
194                                 rwl->writers_waiting--;
195                         }
196                 }
197         }
198
199 #ifdef ISC_RWLOCK_TRACE
200         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
201                                   ISC_MSG_POSTLOCK, "postlock"), rwl, type);
202 #endif
203
204         UNLOCK(&rwl->lock);
205
206         return (result);
207 }
208
209 isc_result_t
210 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
211         return (doit(rwl, type, ISC_FALSE));
212 }
213
214 isc_result_t
215 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
216         return (doit(rwl, type, ISC_TRUE));
217 }
218
219 isc_result_t
220 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
221         isc_result_t result = ISC_R_SUCCESS;
222
223         REQUIRE(VALID_RWLOCK(rwl));
224         LOCK(&rwl->lock);
225         REQUIRE(rwl->type == isc_rwlocktype_read);
226         REQUIRE(rwl->active != 0);
227
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;
233         } else
234                 result = ISC_R_LOCKBUSY;
235
236         UNLOCK(&rwl->lock);
237         return (result);
238 }
239
240 void
241 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
242
243         REQUIRE(VALID_RWLOCK(rwl));
244         LOCK(&rwl->lock);
245         REQUIRE(rwl->type == isc_rwlocktype_write);
246         REQUIRE(rwl->active == 1);
247
248         rwl->type = isc_rwlocktype_read;
249         rwl->original = (rwl->original == isc_rwlocktype_none) ?
250                         isc_rwlocktype_write : isc_rwlocktype_none;
251         /*
252          * Resume processing any read request that were blocked when
253          * we upgraded.
254          */
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);
259
260         UNLOCK(&rwl->lock);
261 }
262
263 isc_result_t
264 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
265
266         REQUIRE(VALID_RWLOCK(rwl));
267         LOCK(&rwl->lock);
268         REQUIRE(rwl->type == type);
269
270         UNUSED(type);
271
272 #ifdef ISC_RWLOCK_TRACE
273         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
274                                   ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
275 #endif
276
277         INSIST(rwl->active > 0);
278         rwl->active--;
279         if (rwl->active == 0) {
280                 if (rwl->original != isc_rwlocktype_none) {
281                         rwl->type = rwl->original;
282                         rwl->original = isc_rwlocktype_none;
283                 }
284                 if (rwl->type == isc_rwlocktype_read) {
285                         rwl->granted = 0;
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);
292                         }
293                 } else {
294                         if (rwl->readers_waiting > 0) {
295                                 if (rwl->writers_waiting > 0 &&
296                                     rwl->granted < rwl->write_quota) {
297                                         SIGNAL(&rwl->writeable);
298                                 } else {
299                                         rwl->granted = 0;
300                                         rwl->type = isc_rwlocktype_read;
301                                         BROADCAST(&rwl->readable);
302                                 }
303                         } else if (rwl->writers_waiting > 0) {
304                                 rwl->granted = 0;
305                                 SIGNAL(&rwl->writeable);
306                         } else {
307                                 rwl->granted = 0;
308                         }
309                 }
310         }
311         INSIST(rwl->original == isc_rwlocktype_none);
312
313 #ifdef ISC_RWLOCK_TRACE
314         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
315                                   ISC_MSG_POSTUNLOCK, "postunlock"),
316                    rwl, type);
317 #endif
318
319         UNLOCK(&rwl->lock);
320
321         return (ISC_R_SUCCESS);
322 }
323
324 void
325 isc_rwlock_destroy(isc_rwlock_t *rwl) {
326         REQUIRE(VALID_RWLOCK(rwl));
327
328         LOCK(&rwl->lock);
329         REQUIRE(rwl->active == 0 &&
330                 rwl->readers_waiting == 0 &&
331                 rwl->writers_waiting == 0);
332         UNLOCK(&rwl->lock);
333
334         rwl->magic = 0;
335         (void)isc_condition_destroy(&rwl->readable);
336         (void)isc_condition_destroy(&rwl->writeable);
337         DESTROYLOCK(&rwl->lock);
338 }
339
340 #else /* ISC_PLATFORM_USETHREADS */
341
342 isc_result_t
343 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
344                 unsigned int write_quota)
345 {
346         REQUIRE(rwl != NULL);
347
348         UNUSED(read_quota);
349         UNUSED(write_quota);
350
351         rwl->type = isc_rwlocktype_read;
352         rwl->active = 0;
353         rwl->magic = RWLOCK_MAGIC;
354
355         return (ISC_R_SUCCESS);
356 }
357
358 isc_result_t
359 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
360         REQUIRE(VALID_RWLOCK(rwl));
361
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;
366                 rwl->active++;
367         } else {
368                 if (rwl->active != 0)
369                         return (ISC_R_LOCKBUSY);
370                 rwl->type = isc_rwlocktype_write;
371                 rwl->active = 1;
372         }
373         return (ISC_R_SUCCESS);
374 }
375
376 isc_result_t
377 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
378         return (isc_rwlock_lock(rwl, type));
379 }
380
381 isc_result_t
382 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
383         isc_result_t result = ISC_R_SUCCESS;
384
385         REQUIRE(VALID_RWLOCK(rwl));
386         REQUIRE(rwl->type == isc_rwlocktype_read);
387         REQUIRE(rwl->active != 0);
388         
389         /* If we are the only reader then succeed. */
390         if (rwl->active == 1)
391                 rwl->type = isc_rwlocktype_write;
392         else
393                 result = ISC_R_LOCKBUSY;
394         return (result);
395 }
396
397 void
398 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
399
400         REQUIRE(VALID_RWLOCK(rwl));
401         REQUIRE(rwl->type == isc_rwlocktype_write);
402         REQUIRE(rwl->active == 1);
403
404         rwl->type = isc_rwlocktype_read;
405 }
406
407 isc_result_t
408 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
409         REQUIRE(VALID_RWLOCK(rwl));
410         REQUIRE(rwl->type == type);
411
412         UNUSED(type);
413
414         INSIST(rwl->active > 0);
415         rwl->active--;
416
417         return (ISC_R_SUCCESS);
418 }
419
420 void
421 isc_rwlock_destroy(isc_rwlock_t *rwl) {
422         REQUIRE(rwl != NULL);
423         REQUIRE(rwl->active == 0);
424         rwl->magic = 0;
425 }
426
427 #endif /* ISC_PLATFORM_USETHREADS */