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>