rwlock.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004, 2005, 2007, 2009, 2011, 2012  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 1998-2001, 2003  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id$ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <stddef.h>
00025 
00026 #include <isc/atomic.h>
00027 #include <isc/magic.h>
00028 #include <isc/msgs.h>
00029 #include <isc/platform.h>
00030 #include <isc/rwlock.h>
00031 #include <isc/util.h>
00032 
00033 #define RWLOCK_MAGIC            ISC_MAGIC('R', 'W', 'L', 'k')
00034 #define VALID_RWLOCK(rwl)       ISC_MAGIC_VALID(rwl, RWLOCK_MAGIC)
00035 
00036 #ifdef ISC_PLATFORM_USETHREADS
00037 
00038 #ifndef RWLOCK_DEFAULT_READ_QUOTA
00039 #define RWLOCK_DEFAULT_READ_QUOTA 4
00040 #endif
00041 
00042 #ifndef RWLOCK_DEFAULT_WRITE_QUOTA
00043 #define RWLOCK_DEFAULT_WRITE_QUOTA 4
00044 #endif
00045 
00046 #ifdef ISC_RWLOCK_TRACE
00047 #include <stdio.h>              /* Required for fprintf/stderr. */
00048 #include <isc/thread.h>         /* Required for isc_thread_self(). */
00049 
00050 static void
00051 print_lock(const char *operation, isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00052         fprintf(stderr,
00053                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00054                                ISC_MSG_PRINTLOCK,
00055                                "rwlock %p thread %lu %s(%s): %s, %u active, "
00056                                "%u granted, %u rwaiting, %u wwaiting\n"),
00057                 rwl, isc_thread_self(), operation,
00058                 (type == isc_rwlocktype_read ?
00059                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00060                                 ISC_MSG_READ, "read") :
00061                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00062                                 ISC_MSG_WRITE, "write")),
00063                 (rwl->type == isc_rwlocktype_read ?
00064                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00065                                 ISC_MSG_READING, "reading") :
00066                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00067                                 ISC_MSG_WRITING, "writing")),
00068                 rwl->active, rwl->granted, rwl->readers_waiting,
00069                 rwl->writers_waiting);
00070 }
00071 #endif
00072 
00073 isc_result_t
00074 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
00075                 unsigned int write_quota)
00076 {
00077         isc_result_t result;
00078 
00079         REQUIRE(rwl != NULL);
00080 
00081         /*
00082          * In case there's trouble initializing, we zero magic now.  If all
00083          * goes well, we'll set it to RWLOCK_MAGIC.
00084          */
00085         rwl->magic = 0;
00086 
00087 #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
00088         rwl->write_requests = 0;
00089         rwl->write_completions = 0;
00090         rwl->cnt_and_flag = 0;
00091         rwl->readers_waiting = 0;
00092         rwl->write_granted = 0;
00093         if (read_quota != 0) {
00094                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00095                                  "read quota is not supported");
00096         }
00097         if (write_quota == 0)
00098                 write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
00099         rwl->write_quota = write_quota;
00100 #else
00101         rwl->type = isc_rwlocktype_read;
00102         rwl->original = isc_rwlocktype_none;
00103         rwl->active = 0;
00104         rwl->granted = 0;
00105         rwl->readers_waiting = 0;
00106         rwl->writers_waiting = 0;
00107         if (read_quota == 0)
00108                 read_quota = RWLOCK_DEFAULT_READ_QUOTA;
00109         rwl->read_quota = read_quota;
00110         if (write_quota == 0)
00111                 write_quota = RWLOCK_DEFAULT_WRITE_QUOTA;
00112         rwl->write_quota = write_quota;
00113 #endif
00114 
00115         result = isc_mutex_init(&rwl->lock);
00116         if (result != ISC_R_SUCCESS)
00117                 return (result);
00118 
00119         result = isc_condition_init(&rwl->readable);
00120         if (result != ISC_R_SUCCESS) {
00121                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00122                                  "isc_condition_init(readable) %s: %s",
00123                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00124                                                 ISC_MSG_FAILED, "failed"),
00125                                  isc_result_totext(result));
00126                 result = ISC_R_UNEXPECTED;
00127                 goto destroy_lock;
00128         }
00129         result = isc_condition_init(&rwl->writeable);
00130         if (result != ISC_R_SUCCESS) {
00131                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00132                                  "isc_condition_init(writeable) %s: %s",
00133                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00134                                                 ISC_MSG_FAILED, "failed"),
00135                                  isc_result_totext(result));
00136                 result = ISC_R_UNEXPECTED;
00137                 goto destroy_rcond;
00138         }
00139 
00140         rwl->magic = RWLOCK_MAGIC;
00141 
00142         return (ISC_R_SUCCESS);
00143 
00144   destroy_rcond:
00145         (void)isc_condition_destroy(&rwl->readable);
00146   destroy_lock:
00147         DESTROYLOCK(&rwl->lock);
00148 
00149         return (result);
00150 }
00151 
00152 void
00153 isc_rwlock_destroy(isc_rwlock_t *rwl) {
00154         REQUIRE(VALID_RWLOCK(rwl));
00155 
00156 #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
00157         REQUIRE(rwl->write_requests == rwl->write_completions &&
00158                 rwl->cnt_and_flag == 0 && rwl->readers_waiting == 0);
00159 #else
00160         LOCK(&rwl->lock);
00161         REQUIRE(rwl->active == 0 &&
00162                 rwl->readers_waiting == 0 &&
00163                 rwl->writers_waiting == 0);
00164         UNLOCK(&rwl->lock);
00165 #endif
00166 
00167         rwl->magic = 0;
00168         (void)isc_condition_destroy(&rwl->readable);
00169         (void)isc_condition_destroy(&rwl->writeable);
00170         DESTROYLOCK(&rwl->lock);
00171 }
00172 
00173 #if defined(ISC_PLATFORM_HAVEXADD) && defined(ISC_PLATFORM_HAVECMPXCHG)
00174 
00175 /*
00176  * When some architecture-dependent atomic operations are available,
00177  * rwlock can be more efficient than the generic algorithm defined below.
00178  * The basic algorithm is described in the following URL:
00179  *   http://www.cs.rochester.edu/u/scott/synchronization/pseudocode/rw.html
00180  *
00181  * The key is to use the following integer variables modified atomically:
00182  *   write_requests, write_completions, and cnt_and_flag.
00183  *
00184  * write_requests and write_completions act as a waiting queue for writers
00185  * in order to ensure the FIFO order.  Both variables begin with the initial
00186  * value of 0.  When a new writer tries to get a write lock, it increments
00187  * write_requests and gets the previous value of the variable as a "ticket".
00188  * When write_completions reaches the ticket number, the new writer can start
00189  * writing.  When the writer completes its work, it increments
00190  * write_completions so that another new writer can start working.  If the
00191  * write_requests is not equal to write_completions, it means a writer is now
00192  * working or waiting.  In this case, a new readers cannot start reading, or
00193  * in other words, this algorithm basically prefers writers.
00194  *
00195  * cnt_and_flag is a "lock" shared by all readers and writers.  This integer
00196  * variable is a kind of structure with two members: writer_flag (1 bit) and
00197  * reader_count (31 bits).  The writer_flag shows whether a writer is working,
00198  * and the reader_count shows the number of readers currently working or almost
00199  * ready for working.  A writer who has the current "ticket" tries to get the
00200  * lock by exclusively setting the writer_flag to 1, provided that the whole
00201  * 32-bit is 0 (meaning no readers or writers working).  On the other hand,
00202  * a new reader tries to increment the "reader_count" field provided that
00203  * the writer_flag is 0 (meaning there is no writer working).
00204  *
00205  * If some of the above operations fail, the reader or the writer sleeps
00206  * until the related condition changes.  When a working reader or writer
00207  * completes its work, some readers or writers are sleeping, and the condition
00208  * that suspended the reader or writer has changed, it wakes up the sleeping
00209  * readers or writers.
00210  *
00211  * As already noted, this algorithm basically prefers writers.  In order to
00212  * prevent readers from starving, however, the algorithm also introduces the
00213  * "writer quota" (Q).  When Q consecutive writers have completed their work,
00214  * suspending readers, the last writer will wake up the readers, even if a new
00215  * writer is waiting.
00216  *
00217  * Implementation specific note: due to the combination of atomic operations
00218  * and a mutex lock, ordering between the atomic operation and locks can be
00219  * very sensitive in some cases.  In particular, it is generally very important
00220  * to check the atomic variable that requires a reader or writer to sleep after
00221  * locking the mutex and before actually sleeping; otherwise, it could be very
00222  * likely to cause a deadlock.  For example, assume "var" is a variable
00223  * atomically modified, then the corresponding code would be:
00224  *      if (var == need_sleep) {
00225  *              LOCK(lock);
00226  *              if (var == need_sleep)
00227  *                      WAIT(cond, lock);
00228  *              UNLOCK(lock);
00229  *      }
00230  * The second check is important, since "var" is protected by the atomic
00231  * operation, not by the mutex, and can be changed just before sleeping.
00232  * (The first "if" could be omitted, but this is also important in order to
00233  * make the code efficient by avoiding the use of the mutex unless it is
00234  * really necessary.)
00235  */
00236 
00237 #define WRITER_ACTIVE   0x1
00238 #define READER_INCR     0x2
00239 
00240 isc_result_t
00241 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00242         isc_int32_t cntflag;
00243 
00244         REQUIRE(VALID_RWLOCK(rwl));
00245 
00246 #ifdef ISC_RWLOCK_TRACE
00247         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00248                                   ISC_MSG_PRELOCK, "prelock"), rwl, type);
00249 #endif
00250 
00251         if (type == isc_rwlocktype_read) {
00252                 if (rwl->write_requests != rwl->write_completions) {
00253                         /* there is a waiting or active writer */
00254                         LOCK(&rwl->lock);
00255                         if (rwl->write_requests != rwl->write_completions) {
00256                                 rwl->readers_waiting++;
00257                                 WAIT(&rwl->readable, &rwl->lock);
00258                                 rwl->readers_waiting--;
00259                         }
00260                         UNLOCK(&rwl->lock);
00261                 }
00262 
00263                 cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
00264                 POST(cntflag);
00265                 while (1) {
00266                         if ((rwl->cnt_and_flag & WRITER_ACTIVE) == 0)
00267                                 break;
00268 
00269                         /* A writer is still working */
00270                         LOCK(&rwl->lock);
00271                         rwl->readers_waiting++;
00272                         if ((rwl->cnt_and_flag & WRITER_ACTIVE) != 0)
00273                                 WAIT(&rwl->readable, &rwl->lock);
00274                         rwl->readers_waiting--;
00275                         UNLOCK(&rwl->lock);
00276 
00277                         /*
00278                          * Typically, the reader should be able to get a lock
00279                          * at this stage:
00280                          *   (1) there should have been no pending writer when
00281                          *       the reader was trying to increment the
00282                          *       counter; otherwise, the writer should be in
00283                          *       the waiting queue, preventing the reader from
00284                          *       proceeding to this point.
00285                          *   (2) once the reader increments the counter, no
00286                          *       more writer can get a lock.
00287                          * Still, it is possible another writer can work at
00288                          * this point, e.g. in the following scenario:
00289                          *   A previous writer unlocks the writer lock.
00290                          *   This reader proceeds to point (1).
00291                          *   A new writer appears, and gets a new lock before
00292                          *   the reader increments the counter.
00293                          *   The reader then increments the counter.
00294                          *   The previous writer notices there is a waiting
00295                          *   reader who is almost ready, and wakes it up.
00296                          * So, the reader needs to confirm whether it can now
00297                          * read explicitly (thus we loop).  Note that this is
00298                          * not an infinite process, since the reader has
00299                          * incremented the counter at this point.
00300                          */
00301                 }
00302 
00303                 /*
00304                  * If we are temporarily preferred to writers due to the writer
00305                  * quota, reset the condition (race among readers doesn't
00306                  * matter).
00307                  */
00308                 rwl->write_granted = 0;
00309         } else {
00310                 isc_int32_t prev_writer;
00311 
00312                 /* enter the waiting queue, and wait for our turn */
00313                 prev_writer = isc_atomic_xadd(&rwl->write_requests, 1);
00314                 while (rwl->write_completions != prev_writer) {
00315                         LOCK(&rwl->lock);
00316                         if (rwl->write_completions != prev_writer) {
00317                                 WAIT(&rwl->writeable, &rwl->lock);
00318                                 UNLOCK(&rwl->lock);
00319                                 continue;
00320                         }
00321                         UNLOCK(&rwl->lock);
00322                         break;
00323                 }
00324 
00325                 while (1) {
00326                         cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,
00327                                                      WRITER_ACTIVE);
00328                         if (cntflag == 0)
00329                                 break;
00330 
00331                         /* Another active reader or writer is working. */
00332                         LOCK(&rwl->lock);
00333                         if (rwl->cnt_and_flag != 0)
00334                                 WAIT(&rwl->writeable, &rwl->lock);
00335                         UNLOCK(&rwl->lock);
00336                 }
00337 
00338                 INSIST((rwl->cnt_and_flag & WRITER_ACTIVE) != 0);
00339                 rwl->write_granted++;
00340         }
00341 
00342 #ifdef ISC_RWLOCK_TRACE
00343         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00344                                   ISC_MSG_POSTLOCK, "postlock"), rwl, type);
00345 #endif
00346 
00347         return (ISC_R_SUCCESS);
00348 }
00349 
00350 isc_result_t
00351 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00352         isc_int32_t cntflag;
00353 
00354         REQUIRE(VALID_RWLOCK(rwl));
00355 
00356 #ifdef ISC_RWLOCK_TRACE
00357         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00358                                   ISC_MSG_PRELOCK, "prelock"), rwl, type);
00359 #endif
00360 
00361         if (type == isc_rwlocktype_read) {
00362                 /* If a writer is waiting or working, we fail. */
00363                 if (rwl->write_requests != rwl->write_completions)
00364                         return (ISC_R_LOCKBUSY);
00365 
00366                 /* Otherwise, be ready for reading. */
00367                 cntflag = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
00368                 if ((cntflag & WRITER_ACTIVE) != 0) {
00369                         /*
00370                          * A writer is working.  We lose, and cancel the read
00371                          * request.
00372                          */
00373                         cntflag = isc_atomic_xadd(&rwl->cnt_and_flag,
00374                                                   -READER_INCR);
00375                         /*
00376                          * If no other readers are waiting and we've suspended
00377                          * new writers in this short period, wake them up.
00378                          */
00379                         if (cntflag == READER_INCR &&
00380                             rwl->write_completions != rwl->write_requests) {
00381                                 LOCK(&rwl->lock);
00382                                 BROADCAST(&rwl->writeable);
00383                                 UNLOCK(&rwl->lock);
00384                         }
00385 
00386                         return (ISC_R_LOCKBUSY);
00387                 }
00388         } else {
00389                 /* Try locking without entering the waiting queue. */
00390                 cntflag = isc_atomic_cmpxchg(&rwl->cnt_and_flag, 0,
00391                                              WRITER_ACTIVE);
00392                 if (cntflag != 0)
00393                         return (ISC_R_LOCKBUSY);
00394 
00395                 /*
00396                  * XXXJT: jump into the queue, possibly breaking the writer
00397                  * order.
00398                  */
00399                 (void)isc_atomic_xadd(&rwl->write_completions, -1);
00400 
00401                 rwl->write_granted++;
00402         }
00403 
00404 #ifdef ISC_RWLOCK_TRACE
00405         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00406                                   ISC_MSG_POSTLOCK, "postlock"), rwl, type);
00407 #endif
00408 
00409         return (ISC_R_SUCCESS);
00410 }
00411 
00412 isc_result_t
00413 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
00414         isc_int32_t prevcnt;
00415 
00416         REQUIRE(VALID_RWLOCK(rwl));
00417 
00418         /* Try to acquire write access. */
00419         prevcnt = isc_atomic_cmpxchg(&rwl->cnt_and_flag,
00420                                      READER_INCR, WRITER_ACTIVE);
00421         /*
00422          * There must have been no writer, and there must have been at least
00423          * one reader.
00424          */
00425         INSIST((prevcnt & WRITER_ACTIVE) == 0 &&
00426                (prevcnt & ~WRITER_ACTIVE) != 0);
00427 
00428         if (prevcnt == READER_INCR) {
00429                 /*
00430                  * We are the only reader and have been upgraded.
00431                  * Now jump into the head of the writer waiting queue.
00432                  */
00433                 (void)isc_atomic_xadd(&rwl->write_completions, -1);
00434         } else
00435                 return (ISC_R_LOCKBUSY);
00436 
00437         return (ISC_R_SUCCESS);
00438 
00439 }
00440 
00441 void
00442 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
00443         isc_int32_t prev_readers;
00444 
00445         REQUIRE(VALID_RWLOCK(rwl));
00446 
00447         /* Become an active reader. */
00448         prev_readers = isc_atomic_xadd(&rwl->cnt_and_flag, READER_INCR);
00449         /* We must have been a writer. */
00450         INSIST((prev_readers & WRITER_ACTIVE) != 0);
00451 
00452         /* Complete write */
00453         (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE);
00454         (void)isc_atomic_xadd(&rwl->write_completions, 1);
00455 
00456         /* Resume other readers */
00457         LOCK(&rwl->lock);
00458         if (rwl->readers_waiting > 0)
00459                 BROADCAST(&rwl->readable);
00460         UNLOCK(&rwl->lock);
00461 }
00462 
00463 isc_result_t
00464 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00465         isc_int32_t prev_cnt;
00466 
00467         REQUIRE(VALID_RWLOCK(rwl));
00468 
00469 #ifdef ISC_RWLOCK_TRACE
00470         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00471                                   ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
00472 #endif
00473 
00474         if (type == isc_rwlocktype_read) {
00475                 prev_cnt = isc_atomic_xadd(&rwl->cnt_and_flag, -READER_INCR);
00476 
00477                 /*
00478                  * If we're the last reader and any writers are waiting, wake
00479                  * them up.  We need to wake up all of them to ensure the
00480                  * FIFO order.
00481                  */
00482                 if (prev_cnt == READER_INCR &&
00483                     rwl->write_completions != rwl->write_requests) {
00484                         LOCK(&rwl->lock);
00485                         BROADCAST(&rwl->writeable);
00486                         UNLOCK(&rwl->lock);
00487                 }
00488         } else {
00489                 isc_boolean_t wakeup_writers = ISC_TRUE;
00490 
00491                 /*
00492                  * Reset the flag, and (implicitly) tell other writers
00493                  * we are done.
00494                  */
00495                 (void)isc_atomic_xadd(&rwl->cnt_and_flag, -WRITER_ACTIVE);
00496                 (void)isc_atomic_xadd(&rwl->write_completions, 1);
00497 
00498                 if (rwl->write_granted >= rwl->write_quota ||
00499                     rwl->write_requests == rwl->write_completions ||
00500                     (rwl->cnt_and_flag & ~WRITER_ACTIVE) != 0) {
00501                         /*
00502                          * We have passed the write quota, no writer is
00503                          * waiting, or some readers are almost ready, pending
00504                          * possible writers.  Note that the last case can
00505                          * happen even if write_requests != write_completions
00506                          * (which means a new writer in the queue), so we need
00507                          * to catch the case explicitly.
00508                          */
00509                         LOCK(&rwl->lock);
00510                         if (rwl->readers_waiting > 0) {
00511                                 wakeup_writers = ISC_FALSE;
00512                                 BROADCAST(&rwl->readable);
00513                         }
00514                         UNLOCK(&rwl->lock);
00515                 }
00516 
00517                 if (rwl->write_requests != rwl->write_completions &&
00518                     wakeup_writers) {
00519                         LOCK(&rwl->lock);
00520                         BROADCAST(&rwl->writeable);
00521                         UNLOCK(&rwl->lock);
00522                 }
00523         }
00524 
00525 #ifdef ISC_RWLOCK_TRACE
00526         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00527                                   ISC_MSG_POSTUNLOCK, "postunlock"),
00528                    rwl, type);
00529 #endif
00530 
00531         return (ISC_R_SUCCESS);
00532 }
00533 
00534 #else /* ISC_PLATFORM_HAVEXADD && ISC_PLATFORM_HAVECMPXCHG */
00535 
00536 static isc_result_t
00537 doit(isc_rwlock_t *rwl, isc_rwlocktype_t type, isc_boolean_t nonblock) {
00538         isc_boolean_t skip = ISC_FALSE;
00539         isc_boolean_t done = ISC_FALSE;
00540         isc_result_t result = ISC_R_SUCCESS;
00541 
00542         REQUIRE(VALID_RWLOCK(rwl));
00543 
00544         LOCK(&rwl->lock);
00545 
00546 #ifdef ISC_RWLOCK_TRACE
00547         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00548                                   ISC_MSG_PRELOCK, "prelock"), rwl, type);
00549 #endif
00550 
00551         if (type == isc_rwlocktype_read) {
00552                 if (rwl->readers_waiting != 0)
00553                         skip = ISC_TRUE;
00554                 while (!done) {
00555                         if (!skip &&
00556                             ((rwl->active == 0 ||
00557                               (rwl->type == isc_rwlocktype_read &&
00558                                (rwl->writers_waiting == 0 ||
00559                                 rwl->granted < rwl->read_quota)))))
00560                         {
00561                                 rwl->type = isc_rwlocktype_read;
00562                                 rwl->active++;
00563                                 rwl->granted++;
00564                                 done = ISC_TRUE;
00565                         } else if (nonblock) {
00566                                 result = ISC_R_LOCKBUSY;
00567                                 done = ISC_TRUE;
00568                         } else {
00569                                 skip = ISC_FALSE;
00570                                 rwl->readers_waiting++;
00571                                 WAIT(&rwl->readable, &rwl->lock);
00572                                 rwl->readers_waiting--;
00573                         }
00574                 }
00575         } else {
00576                 if (rwl->writers_waiting != 0)
00577                         skip = ISC_TRUE;
00578                 while (!done) {
00579                         if (!skip && rwl->active == 0) {
00580                                 rwl->type = isc_rwlocktype_write;
00581                                 rwl->active = 1;
00582                                 rwl->granted++;
00583                                 done = ISC_TRUE;
00584                         } else if (nonblock) {
00585                                 result = ISC_R_LOCKBUSY;
00586                                 done = ISC_TRUE;
00587                         } else {
00588                                 skip = ISC_FALSE;
00589                                 rwl->writers_waiting++;
00590                                 WAIT(&rwl->writeable, &rwl->lock);
00591                                 rwl->writers_waiting--;
00592                         }
00593                 }
00594         }
00595 
00596 #ifdef ISC_RWLOCK_TRACE
00597         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00598                                   ISC_MSG_POSTLOCK, "postlock"), rwl, type);
00599 #endif
00600 
00601         UNLOCK(&rwl->lock);
00602 
00603         return (result);
00604 }
00605 
00606 isc_result_t
00607 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00608         return (doit(rwl, type, ISC_FALSE));
00609 }
00610 
00611 isc_result_t
00612 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00613         return (doit(rwl, type, ISC_TRUE));
00614 }
00615 
00616 isc_result_t
00617 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
00618         isc_result_t result = ISC_R_SUCCESS;
00619 
00620         REQUIRE(VALID_RWLOCK(rwl));
00621         LOCK(&rwl->lock);
00622         REQUIRE(rwl->type == isc_rwlocktype_read);
00623         REQUIRE(rwl->active != 0);
00624 
00625         /* If we are the only reader then succeed. */
00626         if (rwl->active == 1) {
00627                 rwl->original = (rwl->original == isc_rwlocktype_none) ?
00628                                 isc_rwlocktype_read : isc_rwlocktype_none;
00629                 rwl->type = isc_rwlocktype_write;
00630         } else
00631                 result = ISC_R_LOCKBUSY;
00632 
00633         UNLOCK(&rwl->lock);
00634         return (result);
00635 }
00636 
00637 void
00638 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
00639 
00640         REQUIRE(VALID_RWLOCK(rwl));
00641         LOCK(&rwl->lock);
00642         REQUIRE(rwl->type == isc_rwlocktype_write);
00643         REQUIRE(rwl->active == 1);
00644 
00645         rwl->type = isc_rwlocktype_read;
00646         rwl->original = (rwl->original == isc_rwlocktype_none) ?
00647                         isc_rwlocktype_write : isc_rwlocktype_none;
00648         /*
00649          * Resume processing any read request that were blocked when
00650          * we upgraded.
00651          */
00652         if (rwl->original == isc_rwlocktype_none &&
00653             (rwl->writers_waiting == 0 || rwl->granted < rwl->read_quota) &&
00654             rwl->readers_waiting > 0)
00655                 BROADCAST(&rwl->readable);
00656 
00657         UNLOCK(&rwl->lock);
00658 }
00659 
00660 isc_result_t
00661 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00662 
00663         REQUIRE(VALID_RWLOCK(rwl));
00664         LOCK(&rwl->lock);
00665         REQUIRE(rwl->type == type);
00666 
00667         UNUSED(type);
00668 
00669 #ifdef ISC_RWLOCK_TRACE
00670         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00671                                   ISC_MSG_PREUNLOCK, "preunlock"), rwl, type);
00672 #endif
00673 
00674         INSIST(rwl->active > 0);
00675         rwl->active--;
00676         if (rwl->active == 0) {
00677                 if (rwl->original != isc_rwlocktype_none) {
00678                         rwl->type = rwl->original;
00679                         rwl->original = isc_rwlocktype_none;
00680                 }
00681                 if (rwl->type == isc_rwlocktype_read) {
00682                         rwl->granted = 0;
00683                         if (rwl->writers_waiting > 0) {
00684                                 rwl->type = isc_rwlocktype_write;
00685                                 SIGNAL(&rwl->writeable);
00686                         } else if (rwl->readers_waiting > 0) {
00687                                 /* Does this case ever happen? */
00688                                 BROADCAST(&rwl->readable);
00689                         }
00690                 } else {
00691                         if (rwl->readers_waiting > 0) {
00692                                 if (rwl->writers_waiting > 0 &&
00693                                     rwl->granted < rwl->write_quota) {
00694                                         SIGNAL(&rwl->writeable);
00695                                 } else {
00696                                         rwl->granted = 0;
00697                                         rwl->type = isc_rwlocktype_read;
00698                                         BROADCAST(&rwl->readable);
00699                                 }
00700                         } else if (rwl->writers_waiting > 0) {
00701                                 rwl->granted = 0;
00702                                 SIGNAL(&rwl->writeable);
00703                         } else {
00704                                 rwl->granted = 0;
00705                         }
00706                 }
00707         }
00708         INSIST(rwl->original == isc_rwlocktype_none);
00709 
00710 #ifdef ISC_RWLOCK_TRACE
00711         print_lock(isc_msgcat_get(isc_msgcat, ISC_MSGSET_RWLOCK,
00712                                   ISC_MSG_POSTUNLOCK, "postunlock"),
00713                    rwl, type);
00714 #endif
00715 
00716         UNLOCK(&rwl->lock);
00717 
00718         return (ISC_R_SUCCESS);
00719 }
00720 
00721 #endif /* ISC_PLATFORM_HAVEXADD && ISC_PLATFORM_HAVECMPXCHG */
00722 #else /* ISC_PLATFORM_USETHREADS */
00723 
00724 isc_result_t
00725 isc_rwlock_init(isc_rwlock_t *rwl, unsigned int read_quota,
00726                 unsigned int write_quota)
00727 {
00728         REQUIRE(rwl != NULL);
00729 
00730         UNUSED(read_quota);
00731         UNUSED(write_quota);
00732 
00733         rwl->type = isc_rwlocktype_read;
00734         rwl->active = 0;
00735         rwl->magic = RWLOCK_MAGIC;
00736 
00737         return (ISC_R_SUCCESS);
00738 }
00739 
00740 isc_result_t
00741 isc_rwlock_lock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00742         REQUIRE(VALID_RWLOCK(rwl));
00743 
00744         if (type == isc_rwlocktype_read) {
00745                 if (rwl->type != isc_rwlocktype_read && rwl->active != 0)
00746                         return (ISC_R_LOCKBUSY);
00747                 rwl->type = isc_rwlocktype_read;
00748                 rwl->active++;
00749         } else {
00750                 if (rwl->active != 0)
00751                         return (ISC_R_LOCKBUSY);
00752                 rwl->type = isc_rwlocktype_write;
00753                 rwl->active = 1;
00754         }
00755         return (ISC_R_SUCCESS);
00756 }
00757 
00758 isc_result_t
00759 isc_rwlock_trylock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00760         return (isc_rwlock_lock(rwl, type));
00761 }
00762 
00763 isc_result_t
00764 isc_rwlock_tryupgrade(isc_rwlock_t *rwl) {
00765         isc_result_t result = ISC_R_SUCCESS;
00766 
00767         REQUIRE(VALID_RWLOCK(rwl));
00768         REQUIRE(rwl->type == isc_rwlocktype_read);
00769         REQUIRE(rwl->active != 0);
00770 
00771         /* If we are the only reader then succeed. */
00772         if (rwl->active == 1)
00773                 rwl->type = isc_rwlocktype_write;
00774         else
00775                 result = ISC_R_LOCKBUSY;
00776         return (result);
00777 }
00778 
00779 void
00780 isc_rwlock_downgrade(isc_rwlock_t *rwl) {
00781 
00782         REQUIRE(VALID_RWLOCK(rwl));
00783         REQUIRE(rwl->type == isc_rwlocktype_write);
00784         REQUIRE(rwl->active == 1);
00785 
00786         rwl->type = isc_rwlocktype_read;
00787 }
00788 
00789 isc_result_t
00790 isc_rwlock_unlock(isc_rwlock_t *rwl, isc_rwlocktype_t type) {
00791         REQUIRE(VALID_RWLOCK(rwl));
00792         REQUIRE(rwl->type == type);
00793 
00794         UNUSED(type);
00795 
00796         INSIST(rwl->active > 0);
00797         rwl->active--;
00798 
00799         return (ISC_R_SUCCESS);
00800 }
00801 
00802 void
00803 isc_rwlock_destroy(isc_rwlock_t *rwl) {
00804         REQUIRE(rwl != NULL);
00805         REQUIRE(rwl->active == 0);
00806         rwl->magic = 0;
00807 }
00808 
00809 #endif /* ISC_PLATFORM_USETHREADS */

Generated on Tue Apr 28 17:41:05 2015 by Doxygen 1.5.4 for BIND9 Internals 9.11.0pre-alpha