"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.11.23/lib/isc/timer.c" (7 Sep 2020, 28722 Bytes) of package /linux/misc/dns/bind9/9.11.23/bind-9.11.23.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "timer.c" see the Fossies "Dox" file reference documentation.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 
   13 /*! \file */
   14 
   15 #include <config.h>
   16 
   17 #include <stdbool.h>
   18 
   19 #include <isc/app.h>
   20 #include <isc/condition.h>
   21 #include <isc/heap.h>
   22 #include <isc/log.h>
   23 #include <isc/magic.h>
   24 #include <isc/mem.h>
   25 #include <isc/msgs.h>
   26 #include <isc/once.h>
   27 #include <isc/platform.h>
   28 #include <isc/print.h>
   29 #include <isc/task.h>
   30 #include <isc/thread.h>
   31 #include <isc/time.h>
   32 #include <isc/timer.h>
   33 #include <isc/util.h>
   34 
   35 #ifdef OPENSSL_LEAKS
   36 #include <openssl/err.h>
   37 #endif
   38 
   39 /* See task.c about the following definition: */
   40 #ifdef ISC_PLATFORM_USETHREADS
   41 #define USE_TIMER_THREAD
   42 #else
   43 #define USE_SHARED_MANAGER
   44 #endif  /* ISC_PLATFORM_USETHREADS */
   45 
   46 #ifndef USE_TIMER_THREAD
   47 #include "timer_p.h"
   48 #endif /* USE_TIMER_THREAD */
   49 
   50 #ifdef ISC_TIMER_TRACE
   51 #define XTRACE(s)           fprintf(stderr, "%s\n", (s))
   52 #define XTRACEID(s, t)          fprintf(stderr, "%s %p\n", (s), (t))
   53 #define XTRACETIME(s, d)        fprintf(stderr, "%s %u.%09u\n", (s), \
   54                            (d).seconds, (d).nanoseconds)
   55 #define XTRACETIME2(s, d, n)        fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
   56                            (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
   57 #define XTRACETIMER(s, t, d)        fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
   58                            (d).seconds, (d).nanoseconds)
   59 #else
   60 #define XTRACE(s)
   61 #define XTRACEID(s, t)
   62 #define XTRACETIME(s, d)
   63 #define XTRACETIME2(s, d, n)
   64 #define XTRACETIMER(s, t, d)
   65 #endif /* ISC_TIMER_TRACE */
   66 
   67 #define TIMER_MAGIC         ISC_MAGIC('T', 'I', 'M', 'R')
   68 #define VALID_TIMER(t)          ISC_MAGIC_VALID(t, TIMER_MAGIC)
   69 
   70 typedef struct isc__timer isc__timer_t;
   71 typedef struct isc__timermgr isc__timermgr_t;
   72 
   73 struct isc__timer {
   74     /*! Not locked. */
   75     isc_timer_t         common;
   76     isc__timermgr_t *       manager;
   77     isc_mutex_t         lock;
   78     /*! Locked by timer lock. */
   79     unsigned int            references;
   80     isc_time_t          idle;
   81     /*! Locked by manager lock. */
   82     isc_timertype_t         type;
   83     isc_time_t          expires;
   84     isc_interval_t          interval;
   85     isc_task_t *            task;
   86     isc_taskaction_t        action;
   87     void *              arg;
   88     unsigned int            index;
   89     isc_time_t          due;
   90     LINK(isc__timer_t)      link;
   91 };
   92 
   93 #define TIMER_MANAGER_MAGIC     ISC_MAGIC('T', 'I', 'M', 'M')
   94 #define VALID_MANAGER(m)        ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
   95 
   96 struct isc__timermgr {
   97     /* Not locked. */
   98     isc_timermgr_t          common;
   99     isc_mem_t *         mctx;
  100     isc_mutex_t         lock;
  101     /* Locked by manager lock. */
  102     bool            done;
  103     LIST(isc__timer_t)      timers;
  104     unsigned int            nscheduled;
  105     isc_time_t          due;
  106 #ifdef USE_TIMER_THREAD
  107     isc_condition_t         wakeup;
  108     isc_thread_t            thread;
  109 #endif  /* USE_TIMER_THREAD */
  110 #ifdef USE_SHARED_MANAGER
  111     unsigned int            refs;
  112 #endif /* USE_SHARED_MANAGER */
  113     isc_heap_t *            heap;
  114 };
  115 
  116 /*%
  117  * The following are intended for internal use (indicated by "isc__"
  118  * prefix) but are not declared as static, allowing direct access from
  119  * unit tests etc.
  120  */
  121 
  122 isc_result_t
  123 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
  124           const isc_time_t *expires, const isc_interval_t *interval,
  125           isc_task_t *task, isc_taskaction_t action, void *arg,
  126           isc_timer_t **timerp);
  127 isc_result_t
  128 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
  129          const isc_time_t *expires, const isc_interval_t *interval,
  130          bool purge);
  131 isc_timertype_t
  132 isc_timer_gettype(isc_timer_t *timer);
  133 isc_result_t
  134 isc__timer_touch(isc_timer_t *timer);
  135 void
  136 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
  137 void
  138 isc__timer_detach(isc_timer_t **timerp);
  139 isc_result_t
  140 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
  141 void
  142 isc_timermgr_poke(isc_timermgr_t *manager0);
  143 void
  144 isc__timermgr_destroy(isc_timermgr_t **managerp);
  145 
  146 static struct isc__timermethods {
  147     isc_timermethods_t methods;
  148 
  149     /*%
  150      * The following are defined just for avoiding unused static functions.
  151      */
  152     void *gettype;
  153 } timermethods = {
  154     {
  155         isc__timer_attach,
  156         isc__timer_detach,
  157         isc__timer_reset,
  158         isc__timer_touch
  159     },
  160     (void *)isc_timer_gettype
  161 };
  162 
  163 static struct isc__timermgrmethods {
  164     isc_timermgrmethods_t methods;
  165     void *poke;     /* see above */
  166 } timermgrmethods = {
  167     {
  168         isc__timermgr_destroy,
  169         isc__timer_create
  170     },
  171     (void *)isc_timermgr_poke
  172 };
  173 
  174 #ifdef USE_SHARED_MANAGER
  175 /*!
  176  * If the manager is supposed to be shared, there can be only one.
  177  */
  178 static isc__timermgr_t *timermgr = NULL;
  179 #endif /* USE_SHARED_MANAGER */
  180 
  181 static inline isc_result_t
  182 schedule(isc__timer_t *timer, isc_time_t *now, bool signal_ok) {
  183     isc_result_t result;
  184     isc__timermgr_t *manager;
  185     isc_time_t due;
  186     int cmp;
  187 #ifdef USE_TIMER_THREAD
  188     bool timedwait;
  189 #endif
  190 
  191     /*!
  192      * Note: the caller must ensure locking.
  193      */
  194 
  195     REQUIRE(timer->type != isc_timertype_inactive);
  196 
  197 #ifndef USE_TIMER_THREAD
  198     UNUSED(signal_ok);
  199 #endif /* USE_TIMER_THREAD */
  200 
  201     manager = timer->manager;
  202 
  203 #ifdef USE_TIMER_THREAD
  204     /*!
  205      * If the manager was timed wait, we may need to signal the
  206      * manager to force a wakeup.
  207      */
  208     timedwait = (manager->nscheduled > 0 &&
  209              isc_time_seconds(&manager->due) != 0);
  210 #endif
  211 
  212     /*
  213      * Compute the new due time.
  214      */
  215     if (timer->type != isc_timertype_once) {
  216         result = isc_time_add(now, &timer->interval, &due);
  217         if (result != ISC_R_SUCCESS)
  218             return (result);
  219         if (timer->type == isc_timertype_limited &&
  220             isc_time_compare(&timer->expires, &due) < 0)
  221             due = timer->expires;
  222     } else {
  223         if (isc_time_isepoch(&timer->idle))
  224             due = timer->expires;
  225         else if (isc_time_isepoch(&timer->expires))
  226             due = timer->idle;
  227         else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
  228             due = timer->idle;
  229         else
  230             due = timer->expires;
  231     }
  232 
  233     /*
  234      * Schedule the timer.
  235      */
  236 
  237     if (timer->index > 0) {
  238         /*
  239          * Already scheduled.
  240          */
  241         cmp = isc_time_compare(&due, &timer->due);
  242         timer->due = due;
  243         switch (cmp) {
  244         case -1:
  245             isc_heap_increased(manager->heap, timer->index);
  246             break;
  247         case 1:
  248             isc_heap_decreased(manager->heap, timer->index);
  249             break;
  250         case 0:
  251             /* Nothing to do. */
  252             break;
  253         }
  254     } else {
  255         timer->due = due;
  256         result = isc_heap_insert(manager->heap, timer);
  257         if (result != ISC_R_SUCCESS) {
  258             INSIST(result == ISC_R_NOMEMORY);
  259             return (ISC_R_NOMEMORY);
  260         }
  261         manager->nscheduled++;
  262     }
  263 
  264     XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
  265                    ISC_MSG_SCHEDULE, "schedule"), timer, due);
  266 
  267     /*
  268      * If this timer is at the head of the queue, we need to ensure
  269      * that we won't miss it if it has a more recent due time than
  270      * the current "next" timer.  We do this either by waking up the
  271      * run thread, or explicitly setting the value in the manager.
  272      */
  273 #ifdef USE_TIMER_THREAD
  274 
  275     /*
  276      * This is a temporary (probably) hack to fix a bug on tru64 5.1
  277      * and 5.1a.  Sometimes, pthread_cond_timedwait() doesn't actually
  278      * return when the time expires, so here, we check to see if
  279      * we're 15 seconds or more behind, and if we are, we signal
  280      * the dispatcher.  This isn't such a bad idea as a general purpose
  281      * watchdog, so perhaps we should just leave it in here.
  282      */
  283     if (signal_ok && timedwait) {
  284         isc_interval_t fifteen;
  285         isc_time_t then;
  286 
  287         isc_interval_set(&fifteen, 15, 0);
  288         result = isc_time_add(&manager->due, &fifteen, &then);
  289 
  290         if (result == ISC_R_SUCCESS &&
  291             isc_time_compare(&then, now) < 0) {
  292             SIGNAL(&manager->wakeup);
  293             signal_ok = false;
  294             isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
  295                       ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
  296                       "*** POKED TIMER ***");
  297         }
  298     }
  299 
  300     if (timer->index == 1 && signal_ok) {
  301         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
  302                       ISC_MSG_SIGNALSCHED,
  303                       "signal (schedule)"));
  304         SIGNAL(&manager->wakeup);
  305     }
  306 #else /* USE_TIMER_THREAD */
  307     if (timer->index == 1 &&
  308         isc_time_compare(&timer->due, &manager->due) < 0)
  309         manager->due = timer->due;
  310 #endif /* USE_TIMER_THREAD */
  311 
  312     return (ISC_R_SUCCESS);
  313 }
  314 
  315 static inline void
  316 deschedule(isc__timer_t *timer) {
  317 #ifdef USE_TIMER_THREAD
  318     bool need_wakeup = false;
  319 #endif
  320     isc__timermgr_t *manager;
  321 
  322     /*
  323      * The caller must ensure locking.
  324      */
  325 
  326     manager = timer->manager;
  327     if (timer->index > 0) {
  328 #ifdef USE_TIMER_THREAD
  329         if (timer->index == 1)
  330             need_wakeup = true;
  331 #endif
  332         isc_heap_delete(manager->heap, timer->index);
  333         timer->index = 0;
  334         INSIST(manager->nscheduled > 0);
  335         manager->nscheduled--;
  336 #ifdef USE_TIMER_THREAD
  337         if (need_wakeup) {
  338             XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
  339                           ISC_MSG_SIGNALDESCHED,
  340                           "signal (deschedule)"));
  341             SIGNAL(&manager->wakeup);
  342         }
  343 #endif /* USE_TIMER_THREAD */
  344     }
  345 }
  346 
  347 static void
  348 destroy(isc__timer_t *timer) {
  349     isc__timermgr_t *manager = timer->manager;
  350 
  351     /*
  352      * The caller must ensure it is safe to destroy the timer.
  353      */
  354 
  355     LOCK(&manager->lock);
  356 
  357     (void)isc_task_purgerange(timer->task,
  358                   timer,
  359                   ISC_TIMEREVENT_FIRSTEVENT,
  360                   ISC_TIMEREVENT_LASTEVENT,
  361                   NULL);
  362     deschedule(timer);
  363     UNLINK(manager->timers, timer, link);
  364 
  365     UNLOCK(&manager->lock);
  366 
  367     isc_task_detach(&timer->task);
  368     DESTROYLOCK(&timer->lock);
  369     timer->common.impmagic = 0;
  370     timer->common.magic = 0;
  371     isc_mem_put(manager->mctx, timer, sizeof(*timer));
  372 }
  373 
  374 isc_result_t
  375 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
  376           const isc_time_t *expires, const isc_interval_t *interval,
  377           isc_task_t *task, isc_taskaction_t action, void *arg,
  378           isc_timer_t **timerp)
  379 {
  380     isc__timermgr_t *manager;
  381     isc__timer_t *timer;
  382     isc_result_t result;
  383     isc_time_t now;
  384 
  385     REQUIRE(VALID_MANAGER(manager0));
  386     REQUIRE(task != NULL);
  387     REQUIRE(action != NULL);
  388 
  389     /*
  390      * Create a new 'type' timer managed by 'manager'.  The timers
  391      * parameters are specified by 'expires' and 'interval'.  Events
  392      * will be posted to 'task' and when dispatched 'action' will be
  393      * called with 'arg' as the arg value.  The new timer is returned
  394      * in 'timerp'.
  395      */
  396     manager = (isc__timermgr_t *)manager0;
  397     if (expires == NULL)
  398         expires = isc_time_epoch;
  399     if (interval == NULL)
  400         interval = isc_interval_zero;
  401     REQUIRE(type == isc_timertype_inactive ||
  402         !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
  403     REQUIRE(timerp != NULL && *timerp == NULL);
  404     REQUIRE(type != isc_timertype_limited ||
  405         !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
  406 
  407     /*
  408      * Get current time.
  409      */
  410     if (type != isc_timertype_inactive) {
  411         TIME_NOW(&now);
  412     } else {
  413         /*
  414          * We don't have to do this, but it keeps the compiler from
  415          * complaining about "now" possibly being used without being
  416          * set, even though it will never actually happen.
  417          */
  418         isc_time_settoepoch(&now);
  419     }
  420 
  421 
  422     timer = isc_mem_get(manager->mctx, sizeof(*timer));
  423     if (timer == NULL)
  424         return (ISC_R_NOMEMORY);
  425 
  426     timer->manager = manager;
  427     timer->references = 1;
  428 
  429     if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
  430         result = isc_time_add(&now, interval, &timer->idle);
  431         if (result != ISC_R_SUCCESS) {
  432             isc_mem_put(manager->mctx, timer, sizeof(*timer));
  433             return (result);
  434         }
  435     } else
  436         isc_time_settoepoch(&timer->idle);
  437 
  438     timer->type = type;
  439     timer->expires = *expires;
  440     timer->interval = *interval;
  441     timer->task = NULL;
  442     isc_task_attach(task, &timer->task);
  443     timer->action = action;
  444     /*
  445      * Removing the const attribute from "arg" is the best of two
  446      * evils here.  If the timer->arg member is made const, then
  447      * it affects a great many recipients of the timer event
  448      * which did not pass in an "arg" that was truly const.
  449      * Changing isc_timer_create() to not have "arg" prototyped as const,
  450      * though, can cause compilers warnings for calls that *do*
  451      * have a truly const arg.  The caller will have to carefully
  452      * keep track of whether arg started as a true const.
  453      */
  454     DE_CONST(arg, timer->arg);
  455     timer->index = 0;
  456     result = isc_mutex_init(&timer->lock);
  457     if (result != ISC_R_SUCCESS) {
  458         isc_task_detach(&timer->task);
  459         isc_mem_put(manager->mctx, timer, sizeof(*timer));
  460         return (result);
  461     }
  462     ISC_LINK_INIT(timer, link);
  463     timer->common.impmagic = TIMER_MAGIC;
  464     timer->common.magic = ISCAPI_TIMER_MAGIC;
  465     timer->common.methods = (isc_timermethods_t *)&timermethods;
  466 
  467     LOCK(&manager->lock);
  468 
  469     /*
  470      * Note we don't have to lock the timer like we normally would because
  471      * there are no external references to it yet.
  472      */
  473 
  474     if (type != isc_timertype_inactive)
  475         result = schedule(timer, &now, true);
  476     else
  477         result = ISC_R_SUCCESS;
  478     if (result == ISC_R_SUCCESS) {
  479         *timerp = (isc_timer_t *)timer;
  480         APPEND(manager->timers, timer, link);
  481     }
  482 
  483     UNLOCK(&manager->lock);
  484 
  485     if (result != ISC_R_SUCCESS) {
  486         timer->common.impmagic = 0;
  487         timer->common.magic = 0;
  488         DESTROYLOCK(&timer->lock);
  489         isc_task_detach(&timer->task);
  490         isc_mem_put(manager->mctx, timer, sizeof(*timer));
  491         return (result);
  492     }
  493 
  494     return (ISC_R_SUCCESS);
  495 }
  496 
  497 isc_result_t
  498 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
  499          const isc_time_t *expires, const isc_interval_t *interval,
  500          bool purge)
  501 {
  502     isc__timer_t *timer;
  503     isc_time_t now;
  504     isc__timermgr_t *manager;
  505     isc_result_t result;
  506 
  507     /*
  508      * Change the timer's type, expires, and interval values to the given
  509      * values.  If 'purge' is true, any pending events from this timer
  510      * are purged from its task's event queue.
  511      */
  512 
  513     REQUIRE(VALID_TIMER(timer0));
  514     timer = (isc__timer_t *)timer0;
  515     manager = timer->manager;
  516     REQUIRE(VALID_MANAGER(manager));
  517 
  518     if (expires == NULL)
  519         expires = isc_time_epoch;
  520     if (interval == NULL)
  521         interval = isc_interval_zero;
  522     REQUIRE(type == isc_timertype_inactive ||
  523         !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
  524     REQUIRE(type != isc_timertype_limited ||
  525         !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
  526 
  527     /*
  528      * Get current time.
  529      */
  530     if (type != isc_timertype_inactive) {
  531         TIME_NOW(&now);
  532     } else {
  533         /*
  534          * We don't have to do this, but it keeps the compiler from
  535          * complaining about "now" possibly being used without being
  536          * set, even though it will never actually happen.
  537          */
  538         isc_time_settoepoch(&now);
  539     }
  540 
  541     LOCK(&manager->lock);
  542     LOCK(&timer->lock);
  543 
  544     if (purge)
  545         (void)isc_task_purgerange(timer->task,
  546                       timer,
  547                       ISC_TIMEREVENT_FIRSTEVENT,
  548                       ISC_TIMEREVENT_LASTEVENT,
  549                       NULL);
  550     timer->type = type;
  551     timer->expires = *expires;
  552     timer->interval = *interval;
  553     if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
  554         result = isc_time_add(&now, interval, &timer->idle);
  555     } else {
  556         isc_time_settoepoch(&timer->idle);
  557         result = ISC_R_SUCCESS;
  558     }
  559 
  560     if (result == ISC_R_SUCCESS) {
  561         if (type == isc_timertype_inactive) {
  562             deschedule(timer);
  563             result = ISC_R_SUCCESS;
  564         } else
  565             result = schedule(timer, &now, true);
  566     }
  567 
  568     UNLOCK(&timer->lock);
  569     UNLOCK(&manager->lock);
  570 
  571     return (result);
  572 }
  573 
  574 isc_timertype_t
  575 isc_timer_gettype(isc_timer_t *timer0) {
  576     isc__timer_t *timer;
  577     isc_timertype_t t;
  578 
  579     REQUIRE(VALID_TIMER(timer0));
  580     timer = (isc__timer_t *)timer0;
  581 
  582     LOCK(&timer->lock);
  583     t = timer->type;
  584     UNLOCK(&timer->lock);
  585 
  586     return (t);
  587 }
  588 
  589 isc_result_t
  590 isc__timer_touch(isc_timer_t *timer0) {
  591     isc__timer_t *timer;
  592     isc_result_t result;
  593     isc_time_t now;
  594 
  595     /*
  596      * Set the last-touched time of 'timer' to the current time.
  597      */
  598 
  599     REQUIRE(VALID_TIMER(timer0));
  600     timer = (isc__timer_t *)timer0;
  601 
  602     LOCK(&timer->lock);
  603 
  604     /*
  605      * We'd like to
  606      *
  607      *  REQUIRE(timer->type == isc_timertype_once);
  608      *
  609      * but we cannot without locking the manager lock too, which we
  610      * don't want to do.
  611      */
  612 
  613     TIME_NOW(&now);
  614     result = isc_time_add(&now, &timer->interval, &timer->idle);
  615 
  616     UNLOCK(&timer->lock);
  617 
  618     return (result);
  619 }
  620 
  621 void
  622 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
  623     isc__timer_t *timer;
  624 
  625     /*
  626      * Attach *timerp to timer.
  627      */
  628 
  629     REQUIRE(VALID_TIMER(timer0));
  630     timer = (isc__timer_t *)timer0;
  631     REQUIRE(timerp != NULL && *timerp == NULL);
  632 
  633     LOCK(&timer->lock);
  634     timer->references++;
  635     UNLOCK(&timer->lock);
  636 
  637     *timerp = (isc_timer_t *)timer;
  638 }
  639 
  640 void
  641 isc__timer_detach(isc_timer_t **timerp) {
  642     isc__timer_t *timer;
  643     bool free_timer = false;
  644 
  645     /*
  646      * Detach *timerp from its timer.
  647      */
  648 
  649     REQUIRE(timerp != NULL);
  650     timer = (isc__timer_t *)*timerp;
  651     REQUIRE(VALID_TIMER(timer));
  652 
  653     LOCK(&timer->lock);
  654     REQUIRE(timer->references > 0);
  655     timer->references--;
  656     if (timer->references == 0)
  657         free_timer = true;
  658     UNLOCK(&timer->lock);
  659 
  660     if (free_timer)
  661         destroy(timer);
  662 
  663     *timerp = NULL;
  664 }
  665 
  666 static void
  667 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
  668     bool done = false, post_event, need_schedule;
  669     isc_timerevent_t *event;
  670     isc_eventtype_t type = 0;
  671     isc__timer_t *timer;
  672     isc_result_t result;
  673     bool idle;
  674 
  675     /*!
  676      * The caller must be holding the manager lock.
  677      */
  678 
  679     while (manager->nscheduled > 0 && !done) {
  680         timer = isc_heap_element(manager->heap, 1);
  681         INSIST(timer != NULL && timer->type != isc_timertype_inactive);
  682         if (isc_time_compare(now, &timer->due) >= 0) {
  683             if (timer->type == isc_timertype_ticker) {
  684                 type = ISC_TIMEREVENT_TICK;
  685                 post_event = true;
  686                 need_schedule = true;
  687             } else if (timer->type == isc_timertype_limited) {
  688                 int cmp;
  689                 cmp = isc_time_compare(now, &timer->expires);
  690                 if (cmp >= 0) {
  691                     type = ISC_TIMEREVENT_LIFE;
  692                     post_event = true;
  693                     need_schedule = false;
  694                 } else {
  695                     type = ISC_TIMEREVENT_TICK;
  696                     post_event = true;
  697                     need_schedule = true;
  698                 }
  699             } else if (!isc_time_isepoch(&timer->expires) &&
  700                    isc_time_compare(now,
  701                             &timer->expires) >= 0) {
  702                 type = ISC_TIMEREVENT_LIFE;
  703                 post_event = true;
  704                 need_schedule = false;
  705             } else {
  706                 idle = false;
  707 
  708                 LOCK(&timer->lock);
  709                 if (!isc_time_isepoch(&timer->idle) &&
  710                     isc_time_compare(now,
  711                              &timer->idle) >= 0) {
  712                     idle = true;
  713                 }
  714                 UNLOCK(&timer->lock);
  715                 if (idle) {
  716                     type = ISC_TIMEREVENT_IDLE;
  717                     post_event = true;
  718                     need_schedule = false;
  719                 } else {
  720                     /*
  721                      * Idle timer has been touched;
  722                      * reschedule.
  723                      */
  724                     XTRACEID(isc_msgcat_get(isc_msgcat,
  725                                 ISC_MSGSET_TIMER,
  726                                 ISC_MSG_IDLERESCHED,
  727                                 "idle reschedule"),
  728                          timer);
  729                     post_event = false;
  730                     need_schedule = true;
  731                 }
  732             }
  733 
  734             if (post_event) {
  735                 XTRACEID(isc_msgcat_get(isc_msgcat,
  736                             ISC_MSGSET_TIMER,
  737                             ISC_MSG_POSTING,
  738                             "posting"), timer);
  739                 /*
  740                  * XXX We could preallocate this event.
  741                  */
  742                 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
  743                                timer,
  744                                type,
  745                                timer->action,
  746                                timer->arg,
  747                                sizeof(*event));
  748 
  749                 if (event != NULL) {
  750                     event->due = timer->due;
  751                     isc_task_send(timer->task,
  752                               ISC_EVENT_PTR(&event));
  753                 } else
  754                     UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
  755                          isc_msgcat_get(isc_msgcat,
  756                              ISC_MSGSET_TIMER,
  757                              ISC_MSG_EVENTNOTALLOC,
  758                              "couldn't "
  759                              "allocate event"));
  760             }
  761 
  762             timer->index = 0;
  763             isc_heap_delete(manager->heap, 1);
  764             manager->nscheduled--;
  765 
  766             if (need_schedule) {
  767                 result = schedule(timer, now, false);
  768                 if (result != ISC_R_SUCCESS)
  769                     UNEXPECTED_ERROR(__FILE__, __LINE__,
  770                              "%s: %u",
  771                         isc_msgcat_get(isc_msgcat,
  772                             ISC_MSGSET_TIMER,
  773                             ISC_MSG_SCHEDFAIL,
  774                             "couldn't schedule "
  775                             "timer"),
  776                              result);
  777             }
  778         } else {
  779             manager->due = timer->due;
  780             done = true;
  781         }
  782     }
  783 }
  784 
  785 #ifdef USE_TIMER_THREAD
  786 static isc_threadresult_t
  787 #ifdef _WIN32           /* XXXDCL */
  788 WINAPI
  789 #endif
  790 run(void *uap) {
  791     isc__timermgr_t *manager = uap;
  792     isc_time_t now;
  793     isc_result_t result;
  794 
  795     LOCK(&manager->lock);
  796     while (!manager->done) {
  797         TIME_NOW(&now);
  798 
  799         XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
  800                       ISC_MSG_RUNNING,
  801                       "running"), now);
  802 
  803         dispatch(manager, &now);
  804 
  805         if (manager->nscheduled > 0) {
  806             XTRACETIME2(isc_msgcat_get(isc_msgcat,
  807                            ISC_MSGSET_GENERAL,
  808                            ISC_MSG_WAITUNTIL,
  809                            "waituntil"),
  810                     manager->due, now);
  811             result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
  812             INSIST(result == ISC_R_SUCCESS ||
  813                    result == ISC_R_TIMEDOUT);
  814         } else {
  815             XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
  816                           ISC_MSG_WAIT, "wait"), now);
  817             WAIT(&manager->wakeup, &manager->lock);
  818         }
  819         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
  820                       ISC_MSG_WAKEUP, "wakeup"));
  821     }
  822     UNLOCK(&manager->lock);
  823 
  824 #ifdef OPENSSL_LEAKS
  825     ERR_remove_state(0);
  826 #endif
  827 
  828     return ((isc_threadresult_t)0);
  829 }
  830 #endif /* USE_TIMER_THREAD */
  831 
  832 static bool
  833 sooner(void *v1, void *v2) {
  834     isc__timer_t *t1, *t2;
  835 
  836     t1 = v1;
  837     t2 = v2;
  838     REQUIRE(VALID_TIMER(t1));
  839     REQUIRE(VALID_TIMER(t2));
  840 
  841     if (isc_time_compare(&t1->due, &t2->due) < 0)
  842         return (true);
  843     return (false);
  844 }
  845 
  846 static void
  847 set_index(void *what, unsigned int index) {
  848     isc__timer_t *timer;
  849 
  850     REQUIRE(VALID_TIMER(what));
  851     timer = what;
  852 
  853     timer->index = index;
  854 }
  855 
  856 isc_result_t
  857 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
  858     isc__timermgr_t *manager;
  859     isc_result_t result;
  860 
  861     /*
  862      * Create a timer manager.
  863      */
  864 
  865     REQUIRE(managerp != NULL && *managerp == NULL);
  866 
  867 #ifdef USE_SHARED_MANAGER
  868     if (timermgr != NULL) {
  869         timermgr->refs++;
  870         *managerp = (isc_timermgr_t *)timermgr;
  871         return (ISC_R_SUCCESS);
  872     }
  873 #endif /* USE_SHARED_MANAGER */
  874 
  875     manager = isc_mem_get(mctx, sizeof(*manager));
  876     if (manager == NULL)
  877         return (ISC_R_NOMEMORY);
  878 
  879     manager->common.impmagic = TIMER_MANAGER_MAGIC;
  880     manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
  881     manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
  882     manager->mctx = NULL;
  883     manager->done = false;
  884     INIT_LIST(manager->timers);
  885     manager->nscheduled = 0;
  886     isc_time_settoepoch(&manager->due);
  887     manager->heap = NULL;
  888     result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
  889     if (result != ISC_R_SUCCESS) {
  890         INSIST(result == ISC_R_NOMEMORY);
  891         isc_mem_put(mctx, manager, sizeof(*manager));
  892         return (ISC_R_NOMEMORY);
  893     }
  894     result = isc_mutex_init(&manager->lock);
  895     if (result != ISC_R_SUCCESS) {
  896         isc_heap_destroy(&manager->heap);
  897         isc_mem_put(mctx, manager, sizeof(*manager));
  898         return (result);
  899     }
  900     isc_mem_attach(mctx, &manager->mctx);
  901 #ifdef USE_TIMER_THREAD
  902     if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
  903         isc_mem_detach(&manager->mctx);
  904         DESTROYLOCK(&manager->lock);
  905         isc_heap_destroy(&manager->heap);
  906         isc_mem_put(mctx, manager, sizeof(*manager));
  907         UNEXPECTED_ERROR(__FILE__, __LINE__,
  908                  "isc_condition_init() %s",
  909                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
  910                         ISC_MSG_FAILED, "failed"));
  911         return (ISC_R_UNEXPECTED);
  912     }
  913     if (isc_thread_create(run, manager, &manager->thread) !=
  914         ISC_R_SUCCESS) {
  915         isc_mem_detach(&manager->mctx);
  916         (void)isc_condition_destroy(&manager->wakeup);
  917         DESTROYLOCK(&manager->lock);
  918         isc_heap_destroy(&manager->heap);
  919         isc_mem_put(mctx, manager, sizeof(*manager));
  920         UNEXPECTED_ERROR(__FILE__, __LINE__,
  921                  "isc_thread_create() %s",
  922                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
  923                         ISC_MSG_FAILED, "failed"));
  924         return (ISC_R_UNEXPECTED);
  925     }
  926     isc_thread_setname(manager->thread, "isc-timer");
  927 #endif
  928 #ifdef USE_SHARED_MANAGER
  929     manager->refs = 1;
  930     timermgr = manager;
  931 #endif /* USE_SHARED_MANAGER */
  932 
  933     *managerp = (isc_timermgr_t *)manager;
  934 
  935     return (ISC_R_SUCCESS);
  936 }
  937 
  938 void
  939 isc_timermgr_poke(isc_timermgr_t *manager0) {
  940 #ifdef USE_TIMER_THREAD
  941     isc__timermgr_t *manager;
  942 
  943     REQUIRE(VALID_MANAGER(manager0));
  944     manager = (isc__timermgr_t *)manager0;
  945 
  946     SIGNAL(&manager->wakeup);
  947 #else
  948     UNUSED(manager0);
  949 #endif
  950 }
  951 
  952 void
  953 isc__timermgr_destroy(isc_timermgr_t **managerp) {
  954     isc__timermgr_t *manager;
  955     isc_mem_t *mctx;
  956 
  957     /*
  958      * Destroy a timer manager.
  959      */
  960 
  961     REQUIRE(managerp != NULL);
  962     manager = (isc__timermgr_t *)*managerp;
  963     REQUIRE(VALID_MANAGER(manager));
  964 
  965     LOCK(&manager->lock);
  966 
  967 #ifdef USE_SHARED_MANAGER
  968     manager->refs--;
  969     if (manager->refs > 0) {
  970         UNLOCK(&manager->lock);
  971         *managerp = NULL;
  972         return;
  973     }
  974     timermgr = NULL;
  975 #endif /* USE_SHARED_MANAGER */
  976 
  977 #ifndef USE_TIMER_THREAD
  978     isc__timermgr_dispatch((isc_timermgr_t *)manager);
  979 #endif
  980 
  981     REQUIRE(EMPTY(manager->timers));
  982     manager->done = true;
  983 
  984 #ifdef USE_TIMER_THREAD
  985     XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
  986                   ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
  987     SIGNAL(&manager->wakeup);
  988 #endif /* USE_TIMER_THREAD */
  989 
  990     UNLOCK(&manager->lock);
  991 
  992 #ifdef USE_TIMER_THREAD
  993     /*
  994      * Wait for thread to exit.
  995      */
  996     if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
  997         UNEXPECTED_ERROR(__FILE__, __LINE__,
  998                  "isc_thread_join() %s",
  999                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
 1000                         ISC_MSG_FAILED, "failed"));
 1001 #endif /* USE_TIMER_THREAD */
 1002 
 1003     /*
 1004      * Clean up.
 1005      */
 1006 #ifdef USE_TIMER_THREAD
 1007     (void)isc_condition_destroy(&manager->wakeup);
 1008 #endif /* USE_TIMER_THREAD */
 1009     DESTROYLOCK(&manager->lock);
 1010     isc_heap_destroy(&manager->heap);
 1011     manager->common.impmagic = 0;
 1012     manager->common.magic = 0;
 1013     mctx = manager->mctx;
 1014     isc_mem_put(mctx, manager, sizeof(*manager));
 1015     isc_mem_detach(&mctx);
 1016 
 1017     *managerp = NULL;
 1018 
 1019 #ifdef USE_SHARED_MANAGER
 1020     timermgr = NULL;
 1021 #endif
 1022 }
 1023 
 1024 #ifndef USE_TIMER_THREAD
 1025 isc_result_t
 1026 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
 1027     isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
 1028 
 1029 #ifdef USE_SHARED_MANAGER
 1030     if (manager == NULL)
 1031         manager = timermgr;
 1032 #endif
 1033     if (manager == NULL || manager->nscheduled == 0)
 1034         return (ISC_R_NOTFOUND);
 1035     *when = manager->due;
 1036     return (ISC_R_SUCCESS);
 1037 }
 1038 
 1039 void
 1040 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
 1041     isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
 1042     isc_time_t now;
 1043 
 1044 #ifdef USE_SHARED_MANAGER
 1045     if (manager == NULL)
 1046         manager = timermgr;
 1047 #endif
 1048     if (manager == NULL)
 1049         return;
 1050     TIME_NOW(&now);
 1051     dispatch(manager, &now);
 1052 }
 1053 #endif /* USE_TIMER_THREAD */
 1054 
 1055 isc_result_t
 1056 isc__timer_register(void) {
 1057     return (isc_timer_register(isc__timermgr_create));
 1058 }
 1059 
 1060 static isc_mutex_t createlock;
 1061 static isc_once_t once = ISC_ONCE_INIT;
 1062 static isc_timermgrcreatefunc_t timermgr_createfunc = NULL;
 1063 
 1064 static void
 1065 initialize(void) {
 1066     RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS);
 1067 }
 1068 
 1069 isc_result_t
 1070 isc_timer_register(isc_timermgrcreatefunc_t createfunc) {
 1071     isc_result_t result = ISC_R_SUCCESS;
 1072 
 1073     RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
 1074 
 1075     LOCK(&createlock);
 1076     if (timermgr_createfunc == NULL)
 1077         timermgr_createfunc = createfunc;
 1078     else
 1079         result = ISC_R_EXISTS;
 1080     UNLOCK(&createlock);
 1081 
 1082     return (result);
 1083 }
 1084 
 1085 isc_result_t
 1086 isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx,
 1087              isc_timermgr_t **managerp)
 1088 {
 1089     isc_result_t result;
 1090 
 1091     LOCK(&createlock);
 1092 
 1093     REQUIRE(timermgr_createfunc != NULL);
 1094     result = (*timermgr_createfunc)(mctx, managerp);
 1095 
 1096     UNLOCK(&createlock);
 1097 
 1098     if (result == ISC_R_SUCCESS)
 1099         isc_appctx_settimermgr(actx, *managerp);
 1100 
 1101     return (result);
 1102 }
 1103 
 1104 isc_result_t
 1105 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
 1106     isc_result_t result;
 1107 
 1108     if (isc_bind9)
 1109         return (isc__timermgr_create(mctx, managerp));
 1110 
 1111     LOCK(&createlock);
 1112 
 1113     REQUIRE(timermgr_createfunc != NULL);
 1114     result = (*timermgr_createfunc)(mctx, managerp);
 1115 
 1116     UNLOCK(&createlock);
 1117 
 1118     return (result);
 1119 }
 1120 
 1121 void
 1122 isc_timermgr_destroy(isc_timermgr_t **managerp) {
 1123     REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp));
 1124 
 1125     if (isc_bind9)
 1126         isc__timermgr_destroy(managerp);
 1127     else
 1128         (*managerp)->methods->destroy(managerp);
 1129 
 1130     ENSURE(*managerp == NULL);
 1131 }
 1132 
 1133 isc_result_t
 1134 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
 1135          const isc_time_t *expires, const isc_interval_t *interval,
 1136          isc_task_t *task, isc_taskaction_t action, void *arg,
 1137          isc_timer_t **timerp)
 1138 {
 1139     REQUIRE(ISCAPI_TIMERMGR_VALID(manager));
 1140 
 1141     if (isc_bind9)
 1142         return (isc__timer_create(manager, type, expires, interval,
 1143                       task, action, arg, timerp));
 1144 
 1145     return (manager->methods->timercreate(manager, type, expires,
 1146                           interval, task, action, arg,
 1147                           timerp));
 1148 }
 1149 
 1150 void
 1151 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
 1152     REQUIRE(ISCAPI_TIMER_VALID(timer));
 1153     REQUIRE(timerp != NULL && *timerp == NULL);
 1154 
 1155     if (isc_bind9)
 1156         isc__timer_attach(timer, timerp);
 1157     else
 1158         timer->methods->attach(timer, timerp);
 1159 
 1160     ENSURE(*timerp == timer);
 1161 }
 1162 
 1163 void
 1164 isc_timer_detach(isc_timer_t **timerp) {
 1165     REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp));
 1166 
 1167     if (isc_bind9)
 1168         isc__timer_detach(timerp);
 1169     else
 1170         (*timerp)->methods->detach(timerp);
 1171 
 1172     ENSURE(*timerp == NULL);
 1173 }
 1174 
 1175 isc_result_t
 1176 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
 1177         const isc_time_t *expires, const isc_interval_t *interval,
 1178         bool purge)
 1179 {
 1180     REQUIRE(ISCAPI_TIMER_VALID(timer));
 1181 
 1182     if (isc_bind9)
 1183         return (isc__timer_reset(timer, type, expires,
 1184                      interval, purge));
 1185 
 1186     return (timer->methods->reset(timer, type, expires, interval, purge));
 1187 }
 1188 
 1189 isc_result_t
 1190 isc_timer_touch(isc_timer_t *timer) {
 1191     REQUIRE(ISCAPI_TIMER_VALID(timer));
 1192 
 1193     if (isc_bind9)
 1194         return (isc__timer_touch(timer));
 1195 
 1196     return (timer->methods->touch(timer));
 1197 }