Annotation of embedaddon/ntp/lib/isc/rwlock.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2004, 2005, 2007, 2009 Internet Systems Consortium, Inc. ("ISC")
3: * Copyright (C) 1998-2001, 2003 Internet Software Consortium.
4: *
5: * Permission to use, copy, modify, and/or 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.44.332.2 2009/01/18 23:47:41 tbox Exp $ */
19:
20: /*! \file */
21:
22: #include <config.h>
23:
24: #include <stddef.h>
25:
26: #include <isc/atomic.h>
27: #include <isc/magic.h>
28: #include <isc/msgs.h>
29: #include <isc/platform.h>
30: #include <isc/rwlock.h>
31: #include <isc/util.h>
32:
33: #define RWLOCK_MAGIC ISC_MAGIC('R', 'W', 'L', 'k')
34: #define VALID_RWLOCK(rwl) ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)
35:
36: #ifdef ISC_PLATFORM_USETHREADS
37:
38: #ifndef RWLOCK_DEFAULT_READ_QUOTA
39: #define RWLOCK_DEFAULT_READ_QUOTA 4
40: #endif
41:
42: #ifndef RWLOCK_DEFAULT_WRITE_QUOTA
43: #define RWLOCK_DEFAULT_WRITE_QUOTA 4
44: #endif
45:
46: #ifdef ISC_RWLOCK_TRACE
47: #include <stdio.h> /* Required for fprintf/stderr. */
48: #include <isc/thread.h> /* Required for isc_thread_self(). */
49:
50: static void
51: print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
52: fprintf(stderr,
53: isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
54: ISC_MSG_PRINTLOCK,
55: "rwlock %p thread %lu %s(%s): %s, %u active, "
56: "%u granted, %u rwaiting, %u wwaiting\n"),
57: rwl, isc_thread_self(), operation,
58: (type == isc_rwlocktype_read ?
59: isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
60: ISC_MSG_READ, "read") :
61: isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
62: ISC_MSG_WRITE, "write")),
63: (rwl->type == isc_rwlocktype_read ?
64: isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
65: ISC_MSG_READING, "reading") :
66: isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
67: ISC_MSG_WRITING, "writing")),
68: rwl->active, rwl->granted, rwl->readers_waiting,
69: rwl->writers_waiting);
70: }
71: #endif
72:
73: isc_result_t
74: isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
75: unsigned int write_quota)
76: {
77: isc_result_t result;
78:
79: REQUIRE(rwl != NULL);
80:
81: /*
82: * In case there's trouble initializing, we zero magic now. If all
83: * goes well, we'll set it to RWLOCK_MAGIC.
84: */
85: rwl->magic = 0;
86:
87: #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
88: rwl->write_requests = 0;
89: rwl->write_completions = 0;
90: rwl->cnt_and_flag = 0;
91: rwl->readers_waiting = 0;
92: rwl->write_granted = 0;
93: if (read_quota != 0) {
94: UNEXPECTED_ERROR(__FILE__, __LINE__,
95: "read quota is not supported");
96: }
97: if (write_quota == 0)
98: write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
99: rwl->write_quota = write_quota;
100: #else
101: rwl->type = isc_rwlocktype_read;
102: rwl->original = isc_rwlocktype_none;
103: rwl->active = 0;
104: rwl->granted = 0;
105: rwl->readers_waiting = 0;
106: rwl->writers_waiting = 0;
107: if (read_quota == 0)
108: read_quota = RWLOCK_DEFAULT_READ_QUOTA;
109: rwl->read_quota = read_quota;
110: if (write_quota == 0)
111: write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
112: rwl->write_quota = write_quota;
113: #endif
114:
115: result = isc_mutex_init(&rwl->lock);
116: if (result != ISC_R_SUCCESS)
117: return (result);
118:
119: result = isc_condition_init(&rwl->readable);
120: if (result != ISC_R_SUCCESS) {
121: UNEXPECTED_ERROR(__FILE__, __LINE__,
122: "isc_condition_init(readable) %s: %s",
123: isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
124: ISC_MSG_FAILED, "failed"),
125: isc_result_totext(result));
126: result = ISC_R_UNEXPECTED;
127: goto destroy_lock;
128: }
129: result = isc_condition_init(&rwl->writeable);
130: if (result != ISC_R_SUCCESS) {
131: UNEXPECTED_ERROR(__FILE__, __LINE__,
132: "isc_condition_init(writeable) %s: %s",
133: isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
134: ISC_MSG_FAILED, "failed"),
135: isc_result_totext(result));
136: result = ISC_R_UNEXPECTED;
137: goto destroy_rcond;
138: }
139:
140: rwl->magic = RWLOCK_MAGIC;
141:
142: return (ISC_R_SUCCESS);
143:
144: destroy_rcond:
145: (void)isc_condition_destroy(&rwl->readable);
146: destroy_lock:
147: DESTROYLOCK(&rwl->lock);
148:
149: return (result);
150: }
151:
152: void
153: isc_rwlock_destroy(isc_rwlock_t *rwl) {
154: REQUIRE(VALID_RWLOCK(rwl));
155:
156: #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
157: REQUIRE(rwl->write_requests == rwl->write_completions &&
158: rwl->cnt_and_flag == 0 && rwl->readers_waiting == 0);
159: #else
160: LOCK(&rwl->lock);
161: REQUIRE(rwl->active == 0 &&
162: rwl->readers_waiting == 0 &&
163: rwl->writers_waiting == 0);
164: UNLOCK(&rwl->lock);
165: #endif
166:
167: rwl->magic = 0;
168: (void)isc_condition_destroy(&rwl->readable);
169: (void)isc_condition_destroy(&rwl->writeable);
170: DESTROYLOCK(&rwl->lock);
171: }
172:
173: #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
174:
175: /*
176: * When some architecture-dependent atomic operations are available,
177: * rwlock can be more efficient than the generic algorithm defined below.
178: * The basic algorithm is described in the following URL:
179: * http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html
180: *
181: * The key is to use the following integer variables modified atomically:
182: * write_requests, write_completions, and cnt_and_flag.
183: *
184: * write_requests and write_completions act as a waiting queue for writers
185: * in order to ensure the FIFO order. Both variables begin with the initial
186: * value of 0. When a new writer tries to get a write lock, it increments
187: * write_requests and gets the previous value of the variable as a "ticket".
188: * When write_completions reaches the ticket number, the new writer can start
189: * writing. When the writer completes its work, it increments
190: * write_completions so that another new writer can start working. If the
191: * write_requests is not equal to write_completions, it means a writer is now
192: * working or waiting. In this case, a new readers cannot start reading, or
193: * in other words, this algorithm basically prefers writers.
194: *
195: * cnt_and_flag is a "lock" shared by all readers and writers. This integer
196: * variable is a kind of structure with two members: writer_flag (1 bit) and
197: * reader_count (31 bits). The writer_flag shows whether a writer is working,
198: * and the reader_count shows the number of readers currently working or almost
199: * ready for working. A writer who has the current "ticket" tries to get the
200: * lock by exclusively setting the writer_flag to 1, provided that the whole
201: * 32-bit is 0 (meaning no readers or writers working). On the other hand,
202: * a new reader tries to increment the "reader_count" field provided that
203: * the writer_flag is 0 (meaning there is no writer working).
204: *
205: * If some of the above operations fail, the reader or the writer sleeps
206: * until the related condition changes. When a working reader or writer
207: * completes its work, some readers or writers are sleeping, and the condition
208: * that suspended the reader or writer has changed, it wakes up the sleeping
209: * readers or writers.
210: *
211: * As already noted, this algorithm basically prefers writers. In order to
212: * prevent readers from starving, however, the algorithm also introduces the
213: * "writer quota" (Q). When Q consecutive writers have completed their work,
214: * suspending readers, the last writer will wake up the readers, even if a new
215: * writer is waiting.
216: *
217: * Implementation specific note: due to the combination of atomic operations
218: * and a mutex lock, ordering between the atomic operation and locks can be
219: * very sensitive in some cases. In particular, it is generally very important
220: * to check the atomic variable that requires a reader or writer to sleep after
221: * locking the mutex and before actually sleeping; otherwise, it could be very
222: * likely to cause a deadlock. For example, assume "var" is a variable
223: * atomically modified, then the corresponding code would be:
224: * if (var == need_sleep) {
225: * LOCK(lock);
226: * if (var == need_sleep)
227: * WAIT(cond, lock);
228: * UNLOCK(lock);
229: * }
230: * The second check is important, since "var" is protected by the atomic
231: * operation, not by the mutex, and can be changed just before sleeping.
232: * (The first "if" could be omitted, but this is also important in order to
233: * make the code efficient by avoiding the use of the mutex unless it is
234: * really necessary.)
235: */
236:
237: #define WRITER_ACTIVE 0x1
238: #define READER_INCR 0x2
239:
240: isc_result_t
241: isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
242: isc_int32_t cntflag;
243:
244: REQUIRE(VALID_RWLOCK(rwl));
245:
246: #ifdef ISC_RWLOCK_TRACE
247: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
248: ISC_MSG_PRELOCK, "prelock"), rwl, type);
249: #endif
250:
251: if (type == isc_rwlocktype_read) {
252: if (rwl->write_requests != rwl->write_completions) {
253: /* there is a waiting or active writer */
254: LOCK(&rwl->lock);
255: if (rwl->write_requests != rwl->write_completions) {
256: rwl->readers_waiting++;
257: WAIT(&rwl->readable, &rwl->lock);
258: rwl->readers_waiting--;
259: }
260: UNLOCK(&rwl->lock);
261: }
262:
263: cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
264: while (1) {
265: if ((rwl->cnt_and_flag & WRITER_ACTIVE) == 0)
266: break;
267:
268: /* A writer is still working */
269: LOCK(&rwl->lock);
270: rwl->readers_waiting++;
271: if ((rwl->cnt_and_flag & WRITER_ACTIVE) != 0)
272: WAIT(&rwl->readable, &rwl->lock);
273: rwl->readers_waiting--;
274: UNLOCK(&rwl->lock);
275:
276: /*
277: * Typically, the reader should be able to get a lock
278: * at this stage:
279: * (1) there should have been no pending writer when
280: * the reader was trying to increment the
281: * counter; otherwise, the writer should be in
282: * the waiting queue, preventing the reader from
283: * proceeding to this point.
284: * (2) once the reader increments the counter, no
285: * more writer can get a lock.
286: * Still, it is possible another writer can work at
287: * this point, e.g. in the following scenario:
288: * A previous writer unlocks the writer lock.
289: * This reader proceeds to point (1).
290: * A new writer appears, and gets a new lock before
291: * the reader increments the counter.
292: * The reader then increments the counter.
293: * The previous writer notices there is a waiting
294: * reader who is almost ready, and wakes it up.
295: * So, the reader needs to confirm whether it can now
296: * read explicitly (thus we loop). Note that this is
297: * not an infinite process, since the reader has
298: * incremented the counter at this point.
299: */
300: }
301:
302: /*
303: * If we are temporarily preferred to writers due to the writer
304: * quota, reset the condition (race among readers doesn't
305: * matter).
306: */
307: rwl->write_granted = 0;
308: } else {
309: isc_int32_t prev_writer;
310:
311: /* enter the waiting queue, and wait for our turn */
312: prev_writer = isc_atomic_xadd(&rwl->write_requests, 1);
313: while (rwl->write_completions != prev_writer) {
314: LOCK(&rwl->lock);
315: if (rwl->write_completions != prev_writer) {
316: WAIT(&rwl->writeable, &rwl->lock);
317: UNLOCK(&rwl->lock);
318: continue;
319: }
320: UNLOCK(&rwl->lock);
321: break;
322: }
323:
324: while (1) {
325: cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,
326: WRITER_ACTIVE);
327: if (cntflag == 0)
328: break;
329:
330: /* Another active reader or writer is working. */
331: LOCK(&rwl->lock);
332: if (rwl->cnt_and_flag != 0)
333: WAIT(&rwl->writeable, &rwl->lock);
334: UNLOCK(&rwl->lock);
335: }
336:
337: INSIST((rwl->cnt_and_flag & WRITER_ACTIVE) != 0);
338: rwl->write_granted++;
339: }
340:
341: #ifdef ISC_RWLOCK_TRACE
342: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
343: ISC_MSG_POSTLOCK, "postlock"), rwl, type);
344: #endif
345:
346: return (ISC_R_SUCCESS);
347: }
348:
349: isc_result_t
350: isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
351: isc_int32_t cntflag;
352:
353: REQUIRE(VALID_RWLOCK(rwl));
354:
355: #ifdef ISC_RWLOCK_TRACE
356: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
357: ISC_MSG_PRELOCK, "prelock"), rwl, type);
358: #endif
359:
360: if (type == isc_rwlocktype_read) {
361: /* If a writer is waiting or working, we fail. */
362: if (rwl->write_requests != rwl->write_completions)
363: return (ISC_R_LOCKBUSY);
364:
365: /* Otherwise, be ready for reading. */
366: cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
367: if ((cntflag & WRITER_ACTIVE) != 0) {
368: /*
369: * A writer is working. We lose, and cancel the read
370: * request.
371: */
372: cntflag = isc_atomic_xadd(&rwl->cnt_and_flag,
373: -READER_INCR);
374: /*
375: * If no other readers are waiting and we've suspended
376: * new writers in this short period, wake them up.
377: */
378: if (cntflag == READER_INCR &&
379: rwl->write_completions != rwl->write_requests) {
380: LOCK(&rwl->lock);
381: BROADCAST(&rwl->writeable);
382: UNLOCK(&rwl->lock);
383: }
384:
385: return (ISC_R_LOCKBUSY);
386: }
387: } else {
388: /* Try locking without entering the waiting queue. */
389: cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,
390: WRITER_ACTIVE);
391: if (cntflag != 0)
392: return (ISC_R_LOCKBUSY);
393:
394: /*
395: * XXXJT: jump into the queue, possibly breaking the writer
396: * order.
397: */
398: (void)isc_atomic_xadd(&rwl->write_completions, -1);
399:
400: rwl->write_granted++;
401: }
402:
403: #ifdef ISC_RWLOCK_TRACE
404: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
405: ISC_MSG_POSTLOCK, "postlock"), rwl, type);
406: #endif
407:
408: return (ISC_R_SUCCESS);
409: }
410:
411: isc_result_t
412: isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
413: isc_int32_t prevcnt;
414:
415: REQUIRE(VALID_RWLOCK(rwl));
416:
417: /* Try to acquire write access. */
418: prevcnt = isc_atomic_cmpxchg(&rwl->cnt_and_flag,
419: READER_INCR, WRITER_ACTIVE);
420: /*
421: * There must have been no writer, and there must have been at least
422: * one reader.
423: */
424: INSIST((prevcnt & WRITER_ACTIVE) == 0 &&
425: (prevcnt & ~WRITER_ACTIVE) != 0);
426:
427: if (prevcnt == READER_INCR) {
428: /*
429: * We are the only reader and have been upgraded.
430: * Now jump into the head of the writer waiting queue.
431: */
432: (void)isc_atomic_xadd(&rwl->write_completions, -1);
433: } else
434: return (ISC_R_LOCKBUSY);
435:
436: return (ISC_R_SUCCESS);
437:
438: }
439:
440: void
441: isc_rwlock_downgrade(isc_rwlock_t *rwl) {
442: isc_int32_t prev_readers;
443:
444: REQUIRE(VALID_RWLOCK(rwl));
445:
446: /* Become an active reader. */
447: prev_readers = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
448: /* We must have been a writer. */
449: INSIST((prev_readers & WRITER_ACTIVE) != 0);
450:
451: /* Complete write */
452: (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE);
453: (void)isc_atomic_xadd(&rwl->write_completions, 1);
454:
455: /* Resume other readers */
456: LOCK(&rwl->lock);
457: if (rwl->readers_waiting > 0)
458: BROADCAST(&rwl->readable);
459: UNLOCK(&rwl->lock);
460: }
461:
462: isc_result_t
463: isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
464: isc_int32_t prev_cnt;
465:
466: REQUIRE(VALID_RWLOCK(rwl));
467:
468: #ifdef ISC_RWLOCK_TRACE
469: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
470: ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
471: #endif
472:
473: if (type == isc_rwlocktype_read) {
474: prev_cnt = isc_atomic_xadd(&rwl->cnt_and_flag, -READER_INCR);
475:
476: /*
477: * If we're the last reader and any writers are waiting, wake
478: * them up. We need to wake up all of them to ensure the
479: * FIFO order.
480: */
481: if (prev_cnt == READER_INCR &&
482: rwl->write_completions != rwl->write_requests) {
483: LOCK(&rwl->lock);
484: BROADCAST(&rwl->writeable);
485: UNLOCK(&rwl->lock);
486: }
487: } else {
488: isc_boolean_t wakeup_writers = ISC_TRUE;
489:
490: /*
491: * Reset the flag, and (implicitly) tell other writers
492: * we are done.
493: */
494: (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE);
495: (void)isc_atomic_xadd(&rwl->write_completions, 1);
496:
497: if (rwl->write_granted >= rwl->write_quota ||
498: rwl->write_requests == rwl->write_completions ||
499: (rwl->cnt_and_flag & ~WRITER_ACTIVE) != 0) {
500: /*
501: * We have passed the write quota, no writer is
502: * waiting, or some readers are almost ready, pending
503: * possible writers. Note that the last case can
504: * happen even if write_requests != write_completions
505: * (which means a new writer in the queue), so we need
506: * to catch the case explicitly.
507: */
508: LOCK(&rwl->lock);
509: if (rwl->readers_waiting > 0) {
510: wakeup_writers = ISC_FALSE;
511: BROADCAST(&rwl->readable);
512: }
513: UNLOCK(&rwl->lock);
514: }
515:
516: if (rwl->write_requests != rwl->write_completions &&
517: wakeup_writers) {
518: LOCK(&rwl->lock);
519: BROADCAST(&rwl->writeable);
520: UNLOCK(&rwl->lock);
521: }
522: }
523:
524: #ifdef ISC_RWLOCK_TRACE
525: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
526: ISC_MSG_POSTUNLOCK, "postunlock"),
527: rwl, type);
528: #endif
529:
530: return (ISC_R_SUCCESS);
531: }
532:
533: #else /* ISC_PLATFORM_HAVEXADD && ISC_PLATFORM_HAVECMPXCHG */
534:
535: static isc_result_t
536: doit(isc_rwlock_t *rwl, isc_rwlocktype_t type, isc_boolean_t nonblock) {
537: isc_boolean_t skip = ISC_FALSE;
538: isc_boolean_t done = ISC_FALSE;
539: isc_result_t result = ISC_R_SUCCESS;
540:
541: REQUIRE(VALID_RWLOCK(rwl));
542:
543: LOCK(&rwl->lock);
544:
545: #ifdef ISC_RWLOCK_TRACE
546: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
547: ISC_MSG_PRELOCK, "prelock"), rwl, type);
548: #endif
549:
550: if (type == isc_rwlocktype_read) {
551: if (rwl->readers_waiting != 0)
552: skip = ISC_TRUE;
553: while (!done) {
554: if (!skip &&
555: ((rwl->active == 0 ||
556: (rwl->type == isc_rwlocktype_read &&
557: (rwl->writers_waiting == 0 ||
558: rwl->granted < rwl->read_quota)))))
559: {
560: rwl->type = isc_rwlocktype_read;
561: rwl->active++;
562: rwl->granted++;
563: done = ISC_TRUE;
564: } else if (nonblock) {
565: result = ISC_R_LOCKBUSY;
566: done = ISC_TRUE;
567: } else {
568: skip = ISC_FALSE;
569: rwl->readers_waiting++;
570: WAIT(&rwl->readable, &rwl->lock);
571: rwl->readers_waiting--;
572: }
573: }
574: } else {
575: if (rwl->writers_waiting != 0)
576: skip = ISC_TRUE;
577: while (!done) {
578: if (!skip && rwl->active == 0) {
579: rwl->type = isc_rwlocktype_write;
580: rwl->active = 1;
581: rwl->granted++;
582: done = ISC_TRUE;
583: } else if (nonblock) {
584: result = ISC_R_LOCKBUSY;
585: done = ISC_TRUE;
586: } else {
587: skip = ISC_FALSE;
588: rwl->writers_waiting++;
589: WAIT(&rwl->writeable, &rwl->lock);
590: rwl->writers_waiting--;
591: }
592: }
593: }
594:
595: #ifdef ISC_RWLOCK_TRACE
596: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
597: ISC_MSG_POSTLOCK, "postlock"), rwl, type);
598: #endif
599:
600: UNLOCK(&rwl->lock);
601:
602: return (result);
603: }
604:
605: isc_result_t
606: isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
607: return (doit(rwl, type, ISC_FALSE));
608: }
609:
610: isc_result_t
611: isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
612: return (doit(rwl, type, ISC_TRUE));
613: }
614:
615: isc_result_t
616: isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
617: isc_result_t result = ISC_R_SUCCESS;
618:
619: REQUIRE(VALID_RWLOCK(rwl));
620: LOCK(&rwl->lock);
621: REQUIRE(rwl->type == isc_rwlocktype_read);
622: REQUIRE(rwl->active != 0);
623:
624: /* If we are the only reader then succeed. */
625: if (rwl->active == 1) {
626: rwl->original = (rwl->original == isc_rwlocktype_none) ?
627: isc_rwlocktype_read : isc_rwlocktype_none;
628: rwl->type = isc_rwlocktype_write;
629: } else
630: result = ISC_R_LOCKBUSY;
631:
632: UNLOCK(&rwl->lock);
633: return (result);
634: }
635:
636: void
637: isc_rwlock_downgrade(isc_rwlock_t *rwl) {
638:
639: REQUIRE(VALID_RWLOCK(rwl));
640: LOCK(&rwl->lock);
641: REQUIRE(rwl->type == isc_rwlocktype_write);
642: REQUIRE(rwl->active == 1);
643:
644: rwl->type = isc_rwlocktype_read;
645: rwl->original = (rwl->original == isc_rwlocktype_none) ?
646: isc_rwlocktype_write : isc_rwlocktype_none;
647: /*
648: * Resume processing any read request that were blocked when
649: * we upgraded.
650: */
651: if (rwl->original == isc_rwlocktype_none &&
652: (rwl->writers_waiting == 0 || rwl->granted < rwl->read_quota) &&
653: rwl->readers_waiting > 0)
654: BROADCAST(&rwl->readable);
655:
656: UNLOCK(&rwl->lock);
657: }
658:
659: isc_result_t
660: isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
661:
662: REQUIRE(VALID_RWLOCK(rwl));
663: LOCK(&rwl->lock);
664: REQUIRE(rwl->type == type);
665:
666: UNUSED(type);
667:
668: #ifdef ISC_RWLOCK_TRACE
669: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
670: ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
671: #endif
672:
673: INSIST(rwl->active > 0);
674: rwl->active--;
675: if (rwl->active == 0) {
676: if (rwl->original != isc_rwlocktype_none) {
677: rwl->type = rwl->original;
678: rwl->original = isc_rwlocktype_none;
679: }
680: if (rwl->type == isc_rwlocktype_read) {
681: rwl->granted = 0;
682: if (rwl->writers_waiting > 0) {
683: rwl->type = isc_rwlocktype_write;
684: SIGNAL(&rwl->writeable);
685: } else if (rwl->readers_waiting > 0) {
686: /* Does this case ever happen? */
687: BROADCAST(&rwl->readable);
688: }
689: } else {
690: if (rwl->readers_waiting > 0) {
691: if (rwl->writers_waiting > 0 &&
692: rwl->granted < rwl->write_quota) {
693: SIGNAL(&rwl->writeable);
694: } else {
695: rwl->granted = 0;
696: rwl->type = isc_rwlocktype_read;
697: BROADCAST(&rwl->readable);
698: }
699: } else if (rwl->writers_waiting > 0) {
700: rwl->granted = 0;
701: SIGNAL(&rwl->writeable);
702: } else {
703: rwl->granted = 0;
704: }
705: }
706: }
707: INSIST(rwl->original == isc_rwlocktype_none);
708:
709: #ifdef ISC_RWLOCK_TRACE
710: print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
711: ISC_MSG_POSTUNLOCK, "postunlock"),
712: rwl, type);
713: #endif
714:
715: UNLOCK(&rwl->lock);
716:
717: return (ISC_R_SUCCESS);
718: }
719:
720: #endif /* ISC_PLATFORM_HAVEXADD && ISC_PLATFORM_HAVECMPXCHG */
721: #else /* ISC_PLATFORM_USETHREADS */
722:
723: isc_result_t
724: isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
725: unsigned int write_quota)
726: {
727: REQUIRE(rwl != NULL);
728:
729: UNUSED(read_quota);
730: UNUSED(write_quota);
731:
732: rwl->type = isc_rwlocktype_read;
733: rwl->active = 0;
734: rwl->magic = RWLOCK_MAGIC;
735:
736: return (ISC_R_SUCCESS);
737: }
738:
739: isc_result_t
740: isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
741: REQUIRE(VALID_RWLOCK(rwl));
742:
743: if (type == isc_rwlocktype_read) {
744: if (rwl->type != isc_rwlocktype_read && rwl->active != 0)
745: return (ISC_R_LOCKBUSY);
746: rwl->type = isc_rwlocktype_read;
747: rwl->active++;
748: } else {
749: if (rwl->active != 0)
750: return (ISC_R_LOCKBUSY);
751: rwl->type = isc_rwlocktype_write;
752: rwl->active = 1;
753: }
754: return (ISC_R_SUCCESS);
755: }
756:
757: isc_result_t
758: isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
759: return (isc_rwlock_lock(rwl, type));
760: }
761:
762: isc_result_t
763: isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
764: isc_result_t result = ISC_R_SUCCESS;
765:
766: REQUIRE(VALID_RWLOCK(rwl));
767: REQUIRE(rwl->type == isc_rwlocktype_read);
768: REQUIRE(rwl->active != 0);
769:
770: /* If we are the only reader then succeed. */
771: if (rwl->active == 1)
772: rwl->type = isc_rwlocktype_write;
773: else
774: result = ISC_R_LOCKBUSY;
775: return (result);
776: }
777:
778: void
779: isc_rwlock_downgrade(isc_rwlock_t *rwl) {
780:
781: REQUIRE(VALID_RWLOCK(rwl));
782: REQUIRE(rwl->type == isc_rwlocktype_write);
783: REQUIRE(rwl->active == 1);
784:
785: rwl->type = isc_rwlocktype_read;
786: }
787:
788: isc_result_t
789: isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
790: REQUIRE(VALID_RWLOCK(rwl));
791: REQUIRE(rwl->type == type);
792:
793: UNUSED(type);
794:
795: INSIST(rwl->active > 0);
796: rwl->active--;
797:
798: return (ISC_R_SUCCESS);
799: }
800:
801: void
802: isc_rwlock_destroy(isc_rwlock_t *rwl) {
803: REQUIRE(rwl != NULL);
804: REQUIRE(rwl->active == 0);
805: rwl->magic = 0;
806: }
807:
808: #endif /* ISC_PLATFORM_USETHREADS */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>