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