Annotation of embedaddon/ntp/lib/isc/rwlock.c, revision 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>