Merge from vendor branch SENDMAIL:
[dragonfly.git] / contrib / bind-9.2.4rc7 / lib / isc / rwlock.c
1 /*
2  * Copyright (C) 2004  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.5 2004/03/09 06:11:51 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                 return (ISC_R_UNEXPECTED);
113         }
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);
122         }
123
124         rwl->magic = RWLOCK_MAGIC;
125
126         return (ISC_R_SUCCESS);
127 }
128
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;
134
135         REQUIRE(VALID_RWLOCK(rwl));
136
137         LOCK(&rwl->lock);
138
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
143
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)))))
153                         {
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--;
166                         }
167                 }
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--;
185                         }
186                 }
187         }
188
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
193
194         UNLOCK(&rwl->lock);
195
196         return (result);
197 }
198
199 isc_result_t
200 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
201         return (doit(rwl, type, ISC_FALSE));
202 }
203
204 isc_result_t
205 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
206         return (doit(rwl, type, ISC_TRUE));
207 }
208
209 isc_result_t
210 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
211         isc_result_t result = ISC_R_SUCCESS;
212
213         REQUIRE(VALID_RWLOCK(rwl));
214         LOCK(&rwl->lock);
215         REQUIRE(rwl->type == isc_rwlocktype_read);
216         REQUIRE(rwl->active != 0);
217
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;
225
226         UNLOCK(&rwl->lock);
227         return (result);
228 }
229
230 void
231 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
232
233         REQUIRE(VALID_RWLOCK(rwl));
234         LOCK(&rwl->lock);
235         REQUIRE(rwl->type == isc_rwlocktype_write);
236         REQUIRE(rwl->active == 1);
237
238         rwl->type = isc_rwlocktype_read;
239         rwl->original = (rwl->original == isc_rwlocktype_none) ?
240                         isc_rwlocktype_write : isc_rwlocktype_none;
241         /*
242          * Resume processing any read request that were blocked when
243          * we upgraded.
244          */
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);
249
250         UNLOCK(&rwl->lock);
251 }
252
253 isc_result_t
254 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
255
256         REQUIRE(VALID_RWLOCK(rwl));
257         LOCK(&rwl->lock);
258         REQUIRE(rwl->type == type);
259
260         UNUSED(type);
261
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
266
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;
273                 }
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);
282                         }
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);
292                                 }
293                         } else if (rwl->writers_waiting > 0) {
294                                 rwl->granted = 0;
295                                 SIGNAL(&rwl->writeable);
296                         } else {
297                                 rwl->granted = 0;
298                         }
299                 }
300         }
301         INSIST(rwl->original == isc_rwlocktype_none);
302
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
308
309         UNLOCK(&rwl->lock);
310
311         return (ISC_R_SUCCESS);
312 }
313
314 void
315 isc_rwlock_destroy(isc_rwlock_t *rwl) {
316         REQUIRE(VALID_RWLOCK(rwl));
317
318         LOCK(&rwl->lock);
319         REQUIRE(rwl->active == 0 &&
320                 rwl->readers_waiting == 0 &&
321                 rwl->writers_waiting == 0);
322         UNLOCK(&rwl->lock);
323
324         rwl->magic = 0;
325         (void)isc_condition_destroy(&rwl->readable);
326         (void)isc_condition_destroy(&rwl->writeable);
327         DESTROYLOCK(&rwl->lock);
328 }
329
330 #else /* ISC_PLATFORM_USETHREADS */
331
332 isc_result_t
333 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
334                 unsigned int write_quota)
335 {
336         REQUIRE(rwl != NULL);
337
338         UNUSED(read_quota);
339         UNUSED(write_quota);
340
341         rwl->type = isc_rwlocktype_read;
342         rwl->active = 0;
343         rwl->magic = RWLOCK_MAGIC;
344
345         return (ISC_R_SUCCESS);
346 }
347
348 isc_result_t
349 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
350         REQUIRE(VALID_RWLOCK(rwl));
351
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;
362         }
363         return (ISC_R_SUCCESS);
364 }
365
366 isc_result_t
367 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
368         return (isc_rwlock_lock(rwl, type));
369 }
370
371 isc_result_t
372 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
373         isc_result_t result = ISC_R_SUCCESS;
374
375         REQUIRE(VALID_RWLOCK(rwl));
376         REQUIRE(rwl->type == isc_rwlocktype_read);
377         REQUIRE(rwl->active != 0);
378         
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);
385 }
386
387 void
388 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
389
390         REQUIRE(VALID_RWLOCK(rwl));
391         REQUIRE(rwl->type == isc_rwlocktype_write);
392         REQUIRE(rwl->active == 1);
393
394         rwl->type = isc_rwlocktype_read;
395 }
396
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);
401
402         UNUSED(type);
403
404         INSIST(rwl->active > 0);
405         rwl->active--;
406
407         return (ISC_R_SUCCESS);
408 }
409
410 void
411 isc_rwlock_destroy(isc_rwlock_t *rwl) {
412         REQUIRE(rwl != NULL);
413         REQUIRE(rwl->active == 0);
414         rwl->magic = 0;
415 }
416
417 #endif /* ISC_PLATFORM_USETHREADS */