Annotation of embedaddon/ntp/lib/isc/timer.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-2002  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: timer.c,v 1.84.58.4 2009/01/23 23:47:21 tbox Exp $ */
                     19: 
                     20: /*! \file */
                     21: 
                     22: #include <config.h>
                     23: 
                     24: #include <isc/condition.h>
                     25: #include <isc/heap.h>
                     26: #include <isc/log.h>
                     27: #include <isc/magic.h>
                     28: #include <isc/mem.h>
                     29: #include <isc/msgs.h>
                     30: #include <isc/platform.h>
                     31: #include <isc/task.h>
                     32: #include <isc/thread.h>
                     33: #include <isc/time.h>
                     34: #include <isc/timer.h>
                     35: #include <isc/util.h>
                     36: 
                     37: #ifndef ISC_PLATFORM_USETHREADS
                     38: #include "timer_p.h"
                     39: #endif /* ISC_PLATFORM_USETHREADS */
                     40: 
                     41: #ifdef ISC_TIMER_TRACE
                     42: #define XTRACE(s)                      fprintf(stderr, "%s\n", (s))
                     43: #define XTRACEID(s, t)                 fprintf(stderr, "%s %p\n", (s), (t))
                     44: #define XTRACETIME(s, d)               fprintf(stderr, "%s %u.%09u\n", (s), \
                     45:                                               (d).seconds, (d).nanoseconds)
                     46: #define XTRACETIME2(s, d, n)           fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
                     47:                                               (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
                     48: #define XTRACETIMER(s, t, d)           fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
                     49:                                               (d).seconds, (d).nanoseconds)
                     50: #else
                     51: #define XTRACE(s)
                     52: #define XTRACEID(s, t)
                     53: #define XTRACETIME(s, d)
                     54: #define XTRACETIME2(s, d, n)
                     55: #define XTRACETIMER(s, t, d)
                     56: #endif /* ISC_TIMER_TRACE */
                     57: 
                     58: #define TIMER_MAGIC                    ISC_MAGIC('T', 'I', 'M', 'R')
                     59: #define VALID_TIMER(t)                 ISC_MAGIC_VALID(t, TIMER_MAGIC)
                     60: 
                     61: struct isc_timer {
                     62:        /*! Not locked. */
                     63:        unsigned int                    magic;
                     64:        isc_timermgr_t *                manager;
                     65:        isc_mutex_t                     lock;
                     66:        /*! Locked by timer lock. */
                     67:        unsigned int                    references;
                     68:        isc_time_t                      idle;
                     69:        /*! Locked by manager lock. */
                     70:        isc_timertype_t                 type;
                     71:        isc_time_t                      expires;
                     72:        isc_interval_t                  interval;
                     73:        isc_task_t *                    task;
                     74:        isc_taskaction_t                action;
                     75:        void *                          arg;
                     76:        unsigned int                    index;
                     77:        isc_time_t                      due;
                     78:        LINK(isc_timer_t)               link;
                     79: };
                     80: 
                     81: #define TIMER_MANAGER_MAGIC            ISC_MAGIC('T', 'I', 'M', 'M')
                     82: #define VALID_MANAGER(m)               ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
                     83: 
                     84: struct isc_timermgr {
                     85:        /* Not locked. */
                     86:        unsigned int                    magic;
                     87:        isc_mem_t *                     mctx;
                     88:        isc_mutex_t                     lock;
                     89:        /* Locked by manager lock. */
                     90:        isc_boolean_t                   done;
                     91:        LIST(isc_timer_t)               timers;
                     92:        unsigned int                    nscheduled;
                     93:        isc_time_t                      due;
                     94: #ifdef ISC_PLATFORM_USETHREADS
                     95:        isc_condition_t                 wakeup;
                     96:        isc_thread_t                    thread;
                     97: #else /* ISC_PLATFORM_USETHREADS */
                     98:        unsigned int                    refs;
                     99: #endif /* ISC_PLATFORM_USETHREADS */
                    100:        isc_heap_t *                    heap;
                    101: };
                    102: 
                    103: #ifndef ISC_PLATFORM_USETHREADS
                    104: /*!
                    105:  * If threads are not in use, there can be only one.
                    106:  */
                    107: static isc_timermgr_t *timermgr = NULL;
                    108: #endif /* ISC_PLATFORM_USETHREADS */
                    109: 
                    110: static inline isc_result_t
                    111: schedule(isc_timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
                    112:        isc_result_t result;
                    113:        isc_timermgr_t *manager;
                    114:        isc_time_t due;
                    115:        int cmp;
                    116: #ifdef ISC_PLATFORM_USETHREADS
                    117:        isc_boolean_t timedwait;
                    118: #endif
                    119: 
                    120:        /*!
                    121:         * Note: the caller must ensure locking.
                    122:         */
                    123: 
                    124:        REQUIRE(timer->type != isc_timertype_inactive);
                    125: 
                    126: #ifndef ISC_PLATFORM_USETHREADS
                    127:        UNUSED(signal_ok);
                    128: #endif /* ISC_PLATFORM_USETHREADS */
                    129: 
                    130:        manager = timer->manager;
                    131: 
                    132: #ifdef ISC_PLATFORM_USETHREADS
                    133:        /*!
                    134:         * If the manager was timed wait, we may need to signal the
                    135:         * manager to force a wakeup.
                    136:         */
                    137:        timedwait = ISC_TF(manager->nscheduled > 0 &&
                    138:                           isc_time_seconds(&manager->due) != 0);
                    139: #endif
                    140: 
                    141:        /*
                    142:         * Compute the new due time.
                    143:         */
                    144:        if (timer->type != isc_timertype_once) {
                    145:                result = isc_time_add(now, &timer->interval, &due);
                    146:                if (result != ISC_R_SUCCESS)
                    147:                        return (result);
                    148:                if (timer->type == isc_timertype_limited &&
                    149:                    isc_time_compare(&timer->expires, &due) < 0)
                    150:                        due = timer->expires;
                    151:        } else {
                    152:                if (isc_time_isepoch(&timer->idle))
                    153:                        due = timer->expires;
                    154:                else if (isc_time_isepoch(&timer->expires))
                    155:                        due = timer->idle;
                    156:                else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
                    157:                        due = timer->idle;
                    158:                else
                    159:                        due = timer->expires;
                    160:        }
                    161: 
                    162:        /*
                    163:         * Schedule the timer.
                    164:         */
                    165: 
                    166:        if (timer->index > 0) {
                    167:                /*
                    168:                 * Already scheduled.
                    169:                 */
                    170:                cmp = isc_time_compare(&due, &timer->due);
                    171:                timer->due = due;
                    172:                switch (cmp) {
                    173:                case -1:
                    174:                        isc_heap_increased(manager->heap, timer->index);
                    175:                        break;
                    176:                case 1:
                    177:                        isc_heap_decreased(manager->heap, timer->index);
                    178:                        break;
                    179:                case 0:
                    180:                        /* Nothing to do. */
                    181:                        break;
                    182:                }
                    183:        } else {
                    184:                timer->due = due;
                    185:                result = isc_heap_insert(manager->heap, timer);
                    186:                if (result != ISC_R_SUCCESS) {
                    187:                        INSIST(result == ISC_R_NOMEMORY);
                    188:                        return (ISC_R_NOMEMORY);
                    189:                }
                    190:                manager->nscheduled++;
                    191:        }
                    192: 
                    193:        XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
                    194:                                   ISC_MSG_SCHEDULE, "schedule"), timer, due);
                    195: 
                    196:        /*
                    197:         * If this timer is at the head of the queue, we need to ensure
                    198:         * that we won't miss it if it has a more recent due time than
                    199:         * the current "next" timer.  We do this either by waking up the
                    200:         * run thread, or explicitly setting the value in the manager.
                    201:         */
                    202: #ifdef ISC_PLATFORM_USETHREADS
                    203: 
                    204:        /*
                    205:         * This is a temporary (probably) hack to fix a bug on tru64 5.1
                    206:         * and 5.1a.  Sometimes, pthread_cond_timedwait() doesn't actually
                    207:         * return when the time expires, so here, we check to see if
                    208:         * we're 15 seconds or more behind, and if we are, we signal
                    209:         * the dispatcher.  This isn't such a bad idea as a general purpose
                    210:         * watchdog, so perhaps we should just leave it in here.
                    211:         */
                    212:        if (signal_ok && timedwait) {
                    213:                isc_interval_t fifteen;
                    214:                isc_time_t then;
                    215: 
                    216:                isc_interval_set(&fifteen, 15, 0);
                    217:                result = isc_time_add(&manager->due, &fifteen, &then);
                    218: 
                    219:                if (result == ISC_R_SUCCESS &&
                    220:                    isc_time_compare(&then, now) < 0) {
                    221:                        SIGNAL(&manager->wakeup);
                    222:                        signal_ok = ISC_FALSE;
                    223:                        isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
                    224:                                      ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
                    225:                                      "*** POKED TIMER ***");
                    226:                }
                    227:        }
                    228: 
                    229:        if (timer->index == 1 && signal_ok) {
                    230:                XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
                    231:                                      ISC_MSG_SIGNALSCHED,
                    232:                                      "signal (schedule)"));
                    233:                SIGNAL(&manager->wakeup);
                    234:        }
                    235: #else /* ISC_PLATFORM_USETHREADS */
                    236:        if (timer->index == 1 &&
                    237:            isc_time_compare(&timer->due, &manager->due) < 0)
                    238:                manager->due = timer->due;
                    239: #endif /* ISC_PLATFORM_USETHREADS */
                    240: 
                    241:        return (ISC_R_SUCCESS);
                    242: }
                    243: 
                    244: static inline void
                    245: deschedule(isc_timer_t *timer) {
                    246:        isc_boolean_t need_wakeup = ISC_FALSE;
                    247:        isc_timermgr_t *manager;
                    248: 
                    249:        /*
                    250:         * The caller must ensure locking.
                    251:         */
                    252: 
                    253:        manager = timer->manager;
                    254:        if (timer->index > 0) {
                    255:                if (timer->index == 1)
                    256:                        need_wakeup = ISC_TRUE;
                    257:                isc_heap_delete(manager->heap, timer->index);
                    258:                timer->index = 0;
                    259:                INSIST(manager->nscheduled > 0);
                    260:                manager->nscheduled--;
                    261: #ifdef ISC_PLATFORM_USETHREADS
                    262:                if (need_wakeup) {
                    263:                        XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
                    264:                                              ISC_MSG_SIGNALDESCHED,
                    265:                                              "signal (deschedule)"));
                    266:                        SIGNAL(&manager->wakeup);
                    267:                }
                    268: #endif /* ISC_PLATFORM_USETHREADS */
                    269:        }
                    270: }
                    271: 
                    272: static void
                    273: destroy(isc_timer_t *timer) {
                    274:        isc_timermgr_t *manager = timer->manager;
                    275: 
                    276:        /*
                    277:         * The caller must ensure it is safe to destroy the timer.
                    278:         */
                    279: 
                    280:        LOCK(&manager->lock);
                    281: 
                    282:        (void)isc_task_purgerange(timer->task,
                    283:                                  timer,
                    284:                                  ISC_TIMEREVENT_FIRSTEVENT,
                    285:                                  ISC_TIMEREVENT_LASTEVENT,
                    286:                                  NULL);
                    287:        deschedule(timer);
                    288:        UNLINK(manager->timers, timer, link);
                    289: 
                    290:        UNLOCK(&manager->lock);
                    291: 
                    292:        isc_task_detach(&timer->task);
                    293:        DESTROYLOCK(&timer->lock);
                    294:        timer->magic = 0;
                    295:        isc_mem_put(manager->mctx, timer, sizeof(*timer));
                    296: }
                    297: 
                    298: isc_result_t
                    299: isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
                    300:                 isc_time_t *expires, isc_interval_t *interval,
                    301:                 isc_task_t *task, isc_taskaction_t action, const void *arg,
                    302:                 isc_timer_t **timerp)
                    303: {
                    304:        isc_timer_t *timer;
                    305:        isc_result_t result;
                    306:        isc_time_t now;
                    307: 
                    308:        /*
                    309:         * Create a new 'type' timer managed by 'manager'.  The timers
                    310:         * parameters are specified by 'expires' and 'interval'.  Events
                    311:         * will be posted to 'task' and when dispatched 'action' will be
                    312:         * called with 'arg' as the arg value.  The new timer is returned
                    313:         * in 'timerp'.
                    314:         */
                    315: 
                    316:        REQUIRE(VALID_MANAGER(manager));
                    317:        REQUIRE(task != NULL);
                    318:        REQUIRE(action != NULL);
                    319:        if (expires == NULL)
                    320:                expires = isc_time_epoch;
                    321:        if (interval == NULL)
                    322:                interval = isc_interval_zero;
                    323:        REQUIRE(type == isc_timertype_inactive ||
                    324:                !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
                    325:        REQUIRE(timerp != NULL && *timerp == NULL);
                    326:        REQUIRE(type != isc_timertype_limited ||
                    327:                !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
                    328: 
                    329:        /*
                    330:         * Get current time.
                    331:         */
                    332:        if (type != isc_timertype_inactive) {
                    333:                TIME_NOW(&now);
                    334:        } else {
                    335:                /*
                    336:                 * We don't have to do this, but it keeps the compiler from
                    337:                 * complaining about "now" possibly being used without being
                    338:                 * set, even though it will never actually happen.
                    339:                 */
                    340:                isc_time_settoepoch(&now);
                    341:        }
                    342: 
                    343: 
                    344:        timer = isc_mem_get(manager->mctx, sizeof(*timer));
                    345:        if (timer == NULL)
                    346:                return (ISC_R_NOMEMORY);
                    347: 
                    348:        timer->manager = manager;
                    349:        timer->references = 1;
                    350: 
                    351:        if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
                    352:                result = isc_time_add(&now, interval, &timer->idle);
                    353:                if (result != ISC_R_SUCCESS) {
                    354:                        isc_mem_put(manager->mctx, timer, sizeof(*timer));
                    355:                        return (result);
                    356:                }
                    357:        } else
                    358:                isc_time_settoepoch(&timer->idle);
                    359: 
                    360:        timer->type = type;
                    361:        timer->expires = *expires;
                    362:        timer->interval = *interval;
                    363:        timer->task = NULL;
                    364:        isc_task_attach(task, &timer->task);
                    365:        timer->action = action;
                    366:        /*
                    367:         * Removing the const attribute from "arg" is the best of two
                    368:         * evils here.  If the timer->arg member is made const, then
                    369:         * it affects a great many recipients of the timer event
                    370:         * which did not pass in an "arg" that was truly const.
                    371:         * Changing isc_timer_create() to not have "arg" prototyped as const,
                    372:         * though, can cause compilers warnings for calls that *do*
                    373:         * have a truly const arg.  The caller will have to carefully
                    374:         * keep track of whether arg started as a true const.
                    375:         */
                    376:        DE_CONST(arg, timer->arg);
                    377:        timer->index = 0;
                    378:        result = isc_mutex_init(&timer->lock);
                    379:        if (result != ISC_R_SUCCESS) {
                    380:                isc_task_detach(&timer->task);
                    381:                isc_mem_put(manager->mctx, timer, sizeof(*timer));
                    382:                return (result);
                    383:        }
                    384:        ISC_LINK_INIT(timer, link);
                    385:        timer->magic = TIMER_MAGIC;
                    386: 
                    387:        LOCK(&manager->lock);
                    388: 
                    389:        /*
                    390:         * Note we don't have to lock the timer like we normally would because
                    391:         * there are no external references to it yet.
                    392:         */
                    393: 
                    394:        if (type != isc_timertype_inactive)
                    395:                result = schedule(timer, &now, ISC_TRUE);
                    396:        else
                    397:                result = ISC_R_SUCCESS;
                    398:        if (result == ISC_R_SUCCESS)
                    399:                APPEND(manager->timers, timer, link);
                    400: 
                    401:        UNLOCK(&manager->lock);
                    402: 
                    403:        if (result != ISC_R_SUCCESS) {
                    404:                timer->magic = 0;
                    405:                DESTROYLOCK(&timer->lock);
                    406:                isc_task_detach(&timer->task);
                    407:                isc_mem_put(manager->mctx, timer, sizeof(*timer));
                    408:                return (result);
                    409:        }
                    410: 
                    411:        *timerp = timer;
                    412: 
                    413:        return (ISC_R_SUCCESS);
                    414: }
                    415: 
                    416: isc_result_t
                    417: isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
                    418:                isc_time_t *expires, isc_interval_t *interval,
                    419:                isc_boolean_t purge)
                    420: {
                    421:        isc_time_t now;
                    422:        isc_timermgr_t *manager;
                    423:        isc_result_t result;
                    424: 
                    425:        /*
                    426:         * Change the timer's type, expires, and interval values to the given
                    427:         * values.  If 'purge' is ISC_TRUE, any pending events from this timer
                    428:         * are purged from its task's event queue.
                    429:         */
                    430: 
                    431:        REQUIRE(VALID_TIMER(timer));
                    432:        manager = timer->manager;
                    433:        REQUIRE(VALID_MANAGER(manager));
                    434:        if (expires == NULL)
                    435:                expires = isc_time_epoch;
                    436:        if (interval == NULL)
                    437:                interval = isc_interval_zero;
                    438:        REQUIRE(type == isc_timertype_inactive ||
                    439:                !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
                    440:        REQUIRE(type != isc_timertype_limited ||
                    441:                !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
                    442: 
                    443:        /*
                    444:         * Get current time.
                    445:         */
                    446:        if (type != isc_timertype_inactive) {
                    447:                TIME_NOW(&now);
                    448:        } else {
                    449:                /*
                    450:                 * We don't have to do this, but it keeps the compiler from
                    451:                 * complaining about "now" possibly being used without being
                    452:                 * set, even though it will never actually happen.
                    453:                 */
                    454:                isc_time_settoepoch(&now);
                    455:        }
                    456: 
                    457:        manager = timer->manager;
                    458: 
                    459:        LOCK(&manager->lock);
                    460:        LOCK(&timer->lock);
                    461: 
                    462:        if (purge)
                    463:                (void)isc_task_purgerange(timer->task,
                    464:                                          timer,
                    465:                                          ISC_TIMEREVENT_FIRSTEVENT,
                    466:                                          ISC_TIMEREVENT_LASTEVENT,
                    467:                                          NULL);
                    468:        timer->type = type;
                    469:        timer->expires = *expires;
                    470:        timer->interval = *interval;
                    471:        if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
                    472:                result = isc_time_add(&now, interval, &timer->idle);
                    473:        } else {
                    474:                isc_time_settoepoch(&timer->idle);
                    475:                result = ISC_R_SUCCESS;
                    476:        }
                    477: 
                    478:        if (result == ISC_R_SUCCESS) {
                    479:                if (type == isc_timertype_inactive) {
                    480:                        deschedule(timer);
                    481:                        result = ISC_R_SUCCESS;
                    482:                } else
                    483:                        result = schedule(timer, &now, ISC_TRUE);
                    484:        }
                    485: 
                    486:        UNLOCK(&timer->lock);
                    487:        UNLOCK(&manager->lock);
                    488: 
                    489:        return (result);
                    490: }
                    491: 
                    492: isc_timertype_t
                    493: isc_timer_gettype(isc_timer_t *timer) {
                    494:        isc_timertype_t t;
                    495: 
                    496:        REQUIRE(VALID_TIMER(timer));
                    497: 
                    498:        LOCK(&timer->lock);
                    499:        t = timer->type;
                    500:        UNLOCK(&timer->lock);
                    501: 
                    502:        return (t);
                    503: }
                    504: 
                    505: isc_result_t
                    506: isc_timer_touch(isc_timer_t *timer) {
                    507:        isc_result_t result;
                    508:        isc_time_t now;
                    509: 
                    510:        /*
                    511:         * Set the last-touched time of 'timer' to the current time.
                    512:         */
                    513: 
                    514:        REQUIRE(VALID_TIMER(timer));
                    515: 
                    516:        LOCK(&timer->lock);
                    517: 
                    518:        /*
                    519:         * We'd like to
                    520:         *
                    521:         *      REQUIRE(timer->type == isc_timertype_once);
                    522:         *
                    523:         * but we cannot without locking the manager lock too, which we
                    524:         * don't want to do.
                    525:         */
                    526: 
                    527:        TIME_NOW(&now);
                    528:        result = isc_time_add(&now, &timer->interval, &timer->idle);
                    529: 
                    530:        UNLOCK(&timer->lock);
                    531: 
                    532:        return (result);
                    533: }
                    534: 
                    535: void
                    536: isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
                    537:        /*
                    538:         * Attach *timerp to timer.
                    539:         */
                    540: 
                    541:        REQUIRE(VALID_TIMER(timer));
                    542:        REQUIRE(timerp != NULL && *timerp == NULL);
                    543: 
                    544:        LOCK(&timer->lock);
                    545:        timer->references++;
                    546:        UNLOCK(&timer->lock);
                    547: 
                    548:        *timerp = timer;
                    549: }
                    550: 
                    551: void
                    552: isc_timer_detach(isc_timer_t **timerp) {
                    553:        isc_timer_t *timer;
                    554:        isc_boolean_t free_timer = ISC_FALSE;
                    555: 
                    556:        /*
                    557:         * Detach *timerp from its timer.
                    558:         */
                    559: 
                    560:        REQUIRE(timerp != NULL);
                    561:        timer = *timerp;
                    562:        REQUIRE(VALID_TIMER(timer));
                    563: 
                    564:        LOCK(&timer->lock);
                    565:        REQUIRE(timer->references > 0);
                    566:        timer->references--;
                    567:        if (timer->references == 0)
                    568:                free_timer = ISC_TRUE;
                    569:        UNLOCK(&timer->lock);
                    570: 
                    571:        if (free_timer)
                    572:                destroy(timer);
                    573: 
                    574:        *timerp = NULL;
                    575: }
                    576: 
                    577: static void
                    578: dispatch(isc_timermgr_t *manager, isc_time_t *now) {
                    579:        isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
                    580:        isc_timerevent_t *event;
                    581:        isc_eventtype_t type = 0;
                    582:        isc_timer_t *timer;
                    583:        isc_result_t result;
                    584:        isc_boolean_t idle;
                    585: 
                    586:        /*!
                    587:         * The caller must be holding the manager lock.
                    588:         */
                    589: 
                    590:        while (manager->nscheduled > 0 && !done) {
                    591:                timer = isc_heap_element(manager->heap, 1);
                    592:                INSIST(timer->type != isc_timertype_inactive);
                    593:                if (isc_time_compare(now, &timer->due) >= 0) {
                    594:                        if (timer->type == isc_timertype_ticker) {
                    595:                                type = ISC_TIMEREVENT_TICK;
                    596:                                post_event = ISC_TRUE;
                    597:                                need_schedule = ISC_TRUE;
                    598:                        } else if (timer->type == isc_timertype_limited) {
                    599:                                int cmp;
                    600:                                cmp = isc_time_compare(now, &timer->expires);
                    601:                                if (cmp >= 0) {
                    602:                                        type = ISC_TIMEREVENT_LIFE;
                    603:                                        post_event = ISC_TRUE;
                    604:                                        need_schedule = ISC_FALSE;
                    605:                                } else {
                    606:                                        type = ISC_TIMEREVENT_TICK;
                    607:                                        post_event = ISC_TRUE;
                    608:                                        need_schedule = ISC_TRUE;
                    609:                                }
                    610:                        } else if (!isc_time_isepoch(&timer->expires) &&
                    611:                                   isc_time_compare(now,
                    612:                                                    &timer->expires) >= 0) {
                    613:                                type = ISC_TIMEREVENT_LIFE;
                    614:                                post_event = ISC_TRUE;
                    615:                                need_schedule = ISC_FALSE;
                    616:                        } else {
                    617:                                idle = ISC_FALSE;
                    618: 
                    619:                                LOCK(&timer->lock);
                    620:                                if (!isc_time_isepoch(&timer->idle) &&
                    621:                                    isc_time_compare(now,
                    622:                                                     &timer->idle) >= 0) {
                    623:                                        idle = ISC_TRUE;
                    624:                                }
                    625:                                UNLOCK(&timer->lock);
                    626:                                if (idle) {
                    627:                                        type = ISC_TIMEREVENT_IDLE;
                    628:                                        post_event = ISC_TRUE;
                    629:                                        need_schedule = ISC_FALSE;
                    630:                                } else {
                    631:                                        /*
                    632:                                         * Idle timer has been touched;
                    633:                                         * reschedule.
                    634:                                         */
                    635:                                        XTRACEID(isc_msgcat_get(isc_msgcat,
                    636:                                                                ISC_MSGSET_TIMER,
                    637:                                                                ISC_MSG_IDLERESCHED,
                    638:                                                                "idle reschedule"),
                    639:                                                 timer);
                    640:                                        post_event = ISC_FALSE;
                    641:                                        need_schedule = ISC_TRUE;
                    642:                                }
                    643:                        }
                    644: 
                    645:                        if (post_event) {
                    646:                                XTRACEID(isc_msgcat_get(isc_msgcat,
                    647:                                                        ISC_MSGSET_TIMER,
                    648:                                                        ISC_MSG_POSTING,
                    649:                                                        "posting"), timer);
                    650:                                /*
                    651:                                 * XXX We could preallocate this event.
                    652:                                 */
                    653:                                event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
                    654:                                                           timer,
                    655:                                                           type,
                    656:                                                           timer->action,
                    657:                                                           timer->arg,
                    658:                                                           sizeof(*event));
                    659: 
                    660:                                if (event != NULL) {
                    661:                                        event->due = timer->due;
                    662:                                        isc_task_send(timer->task,
                    663:                                                      ISC_EVENT_PTR(&event));
                    664:                                } else
                    665:                                        UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
                    666:                                                 isc_msgcat_get(isc_msgcat,
                    667:                                                         ISC_MSGSET_TIMER,
                    668:                                                         ISC_MSG_EVENTNOTALLOC,
                    669:                                                         "couldn't "
                    670:                                                         "allocate event"));
                    671:                        }
                    672: 
                    673:                        timer->index = 0;
                    674:                        isc_heap_delete(manager->heap, 1);
                    675:                        manager->nscheduled--;
                    676: 
                    677:                        if (need_schedule) {
                    678:                                result = schedule(timer, now, ISC_FALSE);
                    679:                                if (result != ISC_R_SUCCESS)
                    680:                                        UNEXPECTED_ERROR(__FILE__, __LINE__,
                    681:                                                         "%s: %u",
                    682:                                                isc_msgcat_get(isc_msgcat,
                    683:                                                        ISC_MSGSET_TIMER,
                    684:                                                        ISC_MSG_SCHEDFAIL,
                    685:                                                        "couldn't schedule "
                    686:                                                        "timer"),
                    687:                                                         result);
                    688:                        }
                    689:                } else {
                    690:                        manager->due = timer->due;
                    691:                        done = ISC_TRUE;
                    692:                }
                    693:        }
                    694: }
                    695: 
                    696: #ifdef ISC_PLATFORM_USETHREADS
                    697: static isc_threadresult_t
                    698: #ifdef _WIN32                  /* XXXDCL */
                    699: WINAPI
                    700: #endif
                    701: run(void *uap) {
                    702:        isc_timermgr_t *manager = uap;
                    703:        isc_time_t now;
                    704:        isc_result_t result;
                    705: 
                    706:        LOCK(&manager->lock);
                    707:        while (!manager->done) {
                    708:                TIME_NOW(&now);
                    709: 
                    710:                XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    711:                                          ISC_MSG_RUNNING,
                    712:                                          "running"), now);
                    713: 
                    714:                dispatch(manager, &now);
                    715: 
                    716:                if (manager->nscheduled > 0) {
                    717:                        XTRACETIME2(isc_msgcat_get(isc_msgcat,
                    718:                                                   ISC_MSGSET_GENERAL,
                    719:                                                   ISC_MSG_WAITUNTIL,
                    720:                                                   "waituntil"),
                    721:                                    manager->due, now);
                    722:                        result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
                    723:                        INSIST(result == ISC_R_SUCCESS ||
                    724:                               result == ISC_R_TIMEDOUT);
                    725:                } else {
                    726:                        XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    727:                                                  ISC_MSG_WAIT, "wait"), now);
                    728:                        WAIT(&manager->wakeup, &manager->lock);
                    729:                }
                    730:                XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
                    731:                                      ISC_MSG_WAKEUP, "wakeup"));
                    732:        }
                    733:        UNLOCK(&manager->lock);
                    734: 
                    735:        return ((isc_threadresult_t)0);
                    736: }
                    737: #endif /* ISC_PLATFORM_USETHREADS */
                    738: 
                    739: static isc_boolean_t
                    740: sooner(void *v1, void *v2) {
                    741:        isc_timer_t *t1, *t2;
                    742: 
                    743:        t1 = v1;
                    744:        t2 = v2;
                    745:        REQUIRE(VALID_TIMER(t1));
                    746:        REQUIRE(VALID_TIMER(t2));
                    747: 
                    748:        if (isc_time_compare(&t1->due, &t2->due) < 0)
                    749:                return (ISC_TRUE);
                    750:        return (ISC_FALSE);
                    751: }
                    752: 
                    753: static void
                    754: set_index(void *what, unsigned int index) {
                    755:        isc_timer_t *timer;
                    756: 
                    757:        timer = what;
                    758:        REQUIRE(VALID_TIMER(timer));
                    759: 
                    760:        timer->index = index;
                    761: }
                    762: 
                    763: isc_result_t
                    764: isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
                    765:        isc_timermgr_t *manager;
                    766:        isc_result_t result;
                    767: 
                    768:        /*
                    769:         * Create a timer manager.
                    770:         */
                    771: 
                    772:        REQUIRE(managerp != NULL && *managerp == NULL);
                    773: 
                    774: #ifndef ISC_PLATFORM_USETHREADS
                    775:        if (timermgr != NULL) {
                    776:                timermgr->refs++;
                    777:                *managerp = timermgr;
                    778:                return (ISC_R_SUCCESS);
                    779:        }
                    780: #endif /* ISC_PLATFORM_USETHREADS */
                    781: 
                    782:        manager = isc_mem_get(mctx, sizeof(*manager));
                    783:        if (manager == NULL)
                    784:                return (ISC_R_NOMEMORY);
                    785: 
                    786:        manager->magic = TIMER_MANAGER_MAGIC;
                    787:        manager->mctx = NULL;
                    788:        manager->done = ISC_FALSE;
                    789:        INIT_LIST(manager->timers);
                    790:        manager->nscheduled = 0;
                    791:        isc_time_settoepoch(&manager->due);
                    792:        manager->heap = NULL;
                    793:        result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
                    794:        if (result != ISC_R_SUCCESS) {
                    795:                INSIST(result == ISC_R_NOMEMORY);
                    796:                isc_mem_put(mctx, manager, sizeof(*manager));
                    797:                return (ISC_R_NOMEMORY);
                    798:        }
                    799:        result = isc_mutex_init(&manager->lock);
                    800:        if (result != ISC_R_SUCCESS) {
                    801:                isc_heap_destroy(&manager->heap);
                    802:                isc_mem_put(mctx, manager, sizeof(*manager));
                    803:                return (result);
                    804:        }
                    805:        isc_mem_attach(mctx, &manager->mctx);
                    806: #ifdef ISC_PLATFORM_USETHREADS
                    807:        if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
                    808:                isc_mem_detach(&manager->mctx);
                    809:                DESTROYLOCK(&manager->lock);
                    810:                isc_heap_destroy(&manager->heap);
                    811:                isc_mem_put(mctx, manager, sizeof(*manager));
                    812:                UNEXPECTED_ERROR(__FILE__, __LINE__,
                    813:                                 "isc_condition_init() %s",
                    814:                                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    815:                                                ISC_MSG_FAILED, "failed"));
                    816:                return (ISC_R_UNEXPECTED);
                    817:        }
                    818:        if (isc_thread_create(run, manager, &manager->thread) !=
                    819:            ISC_R_SUCCESS) {
                    820:                isc_mem_detach(&manager->mctx);
                    821:                (void)isc_condition_destroy(&manager->wakeup);
                    822:                DESTROYLOCK(&manager->lock);
                    823:                isc_heap_destroy(&manager->heap);
                    824:                isc_mem_put(mctx, manager, sizeof(*manager));
                    825:                UNEXPECTED_ERROR(__FILE__, __LINE__,
                    826:                                 "isc_thread_create() %s",
                    827:                                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    828:                                                ISC_MSG_FAILED, "failed"));
                    829:                return (ISC_R_UNEXPECTED);
                    830:        }
                    831: #else /* ISC_PLATFORM_USETHREADS */
                    832:        manager->refs = 1;
                    833:        timermgr = manager;
                    834: #endif /* ISC_PLATFORM_USETHREADS */
                    835: 
                    836:        *managerp = manager;
                    837: 
                    838:        return (ISC_R_SUCCESS);
                    839: }
                    840: 
                    841: void
                    842: isc_timermgr_poke(isc_timermgr_t *manager) {
                    843: #ifdef ISC_PLATFORM_USETHREADS
                    844:        REQUIRE(VALID_MANAGER(manager));
                    845: 
                    846:        SIGNAL(&manager->wakeup);
                    847: #else
                    848:        UNUSED(manager);
                    849: #endif
                    850: }
                    851: 
                    852: void
                    853: isc_timermgr_destroy(isc_timermgr_t **managerp) {
                    854:        isc_timermgr_t *manager;
                    855:        isc_mem_t *mctx;
                    856: 
                    857:        /*
                    858:         * Destroy a timer manager.
                    859:         */
                    860: 
                    861:        REQUIRE(managerp != NULL);
                    862:        manager = *managerp;
                    863:        REQUIRE(VALID_MANAGER(manager));
                    864: 
                    865:        LOCK(&manager->lock);
                    866: 
                    867: #ifndef ISC_PLATFORM_USETHREADS
                    868:        if (manager->refs > 1) {
                    869:                manager->refs--;
                    870:                UNLOCK(&manager->lock);
                    871:                *managerp = NULL;
                    872:                return;
                    873:        }
                    874: 
                    875:        isc__timermgr_dispatch();
                    876: #endif /* ISC_PLATFORM_USETHREADS */
                    877: 
                    878:        REQUIRE(EMPTY(manager->timers));
                    879:        manager->done = ISC_TRUE;
                    880: 
                    881: #ifdef ISC_PLATFORM_USETHREADS
                    882:        XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
                    883:                              ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
                    884:        SIGNAL(&manager->wakeup);
                    885: #endif /* ISC_PLATFORM_USETHREADS */
                    886: 
                    887:        UNLOCK(&manager->lock);
                    888: 
                    889: #ifdef ISC_PLATFORM_USETHREADS
                    890:        /*
                    891:         * Wait for thread to exit.
                    892:         */
                    893:        if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
                    894:                UNEXPECTED_ERROR(__FILE__, __LINE__,
                    895:                                 "isc_thread_join() %s",
                    896:                                 isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
                    897:                                                ISC_MSG_FAILED, "failed"));
                    898: #endif /* ISC_PLATFORM_USETHREADS */
                    899: 
                    900:        /*
                    901:         * Clean up.
                    902:         */
                    903: #ifdef ISC_PLATFORM_USETHREADS
                    904:        (void)isc_condition_destroy(&manager->wakeup);
                    905: #endif /* ISC_PLATFORM_USETHREADS */
                    906:        DESTROYLOCK(&manager->lock);
                    907:        isc_heap_destroy(&manager->heap);
                    908:        manager->magic = 0;
                    909:        mctx = manager->mctx;
                    910:        isc_mem_put(mctx, manager, sizeof(*manager));
                    911:        isc_mem_detach(&mctx);
                    912: 
                    913:        *managerp = NULL;
                    914: }
                    915: 
                    916: #ifndef ISC_PLATFORM_USETHREADS
                    917: isc_result_t
                    918: isc__timermgr_nextevent(isc_time_t *when) {
                    919:        if (timermgr == NULL || timermgr->nscheduled == 0)
                    920:                return (ISC_R_NOTFOUND);
                    921:        *when = timermgr->due;
                    922:        return (ISC_R_SUCCESS);
                    923: }
                    924: 
                    925: void
                    926: isc__timermgr_dispatch(void) {
                    927:        isc_time_t now;
                    928:        if (timermgr == NULL)
                    929:                return;
                    930:        TIME_NOW(&now);
                    931:        dispatch(timermgr, &now);
                    932: }
                    933: #endif /* ISC_PLATFORM_USETHREADS */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>