timer.c

Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2004, 2005, 2007-2009, 2011-2014  Internet Systems Consortium, Inc. ("ISC")
00003  * Copyright (C) 1998-2002  Internet Software Consortium.
00004  *
00005  * Permission to use, copy, modify, and/or distribute this software for any
00006  * purpose with or without fee is hereby granted, provided that the above
00007  * copyright notice and this permission notice appear in all copies.
00008  *
00009  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
00010  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
00011  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
00012  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
00013  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
00014  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
00015  * PERFORMANCE OF THIS SOFTWARE.
00016  */
00017 
00018 /* $Id$ */
00019 
00020 /*! \file */
00021 
00022 #include <config.h>
00023 
00024 #include <isc/app.h>
00025 #include <isc/condition.h>
00026 #include <isc/heap.h>
00027 #include <isc/log.h>
00028 #include <isc/magic.h>
00029 #include <isc/mem.h>
00030 #include <isc/msgs.h>
00031 #include <isc/once.h>
00032 #include <isc/platform.h>
00033 #include <isc/task.h>
00034 #include <isc/thread.h>
00035 #include <isc/time.h>
00036 #include <isc/timer.h>
00037 #include <isc/util.h>
00038 
00039 #ifdef OPENSSL_LEAKS
00040 #include <openssl/err.h>
00041 #endif
00042 
00043 /* See task.c about the following definition: */
00044 #ifdef ISC_PLATFORM_USETHREADS
00045 #define USE_TIMER_THREAD
00046 #else
00047 #define USE_SHARED_MANAGER
00048 #endif  /* ISC_PLATFORM_USETHREADS */
00049 
00050 #ifndef USE_TIMER_THREAD
00051 #include "timer_p.h"
00052 #endif /* USE_TIMER_THREAD */
00053 
00054 #ifdef ISC_TIMER_TRACE
00055 #define XTRACE(s)                       fprintf(stderr, "%s\n", (s))
00056 #define XTRACEID(s, t)                  fprintf(stderr, "%s %p\n", (s), (t))
00057 #define XTRACETIME(s, d)                fprintf(stderr, "%s %u.%09u\n", (s), \
00058                                                (d).seconds, (d).nanoseconds)
00059 #define XTRACETIME2(s, d, n)            fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), \
00060                                                (d).seconds, (d).nanoseconds, (n).seconds, (n).nanoseconds)
00061 #define XTRACETIMER(s, t, d)            fprintf(stderr, "%s %p %u.%09u\n", (s), (t), \
00062                                                (d).seconds, (d).nanoseconds)
00063 #else
00064 #define XTRACE(s)
00065 #define XTRACEID(s, t)
00066 #define XTRACETIME(s, d)
00067 #define XTRACETIME2(s, d, n)
00068 #define XTRACETIMER(s, t, d)
00069 #endif /* ISC_TIMER_TRACE */
00070 
00071 #define TIMER_MAGIC                     ISC_MAGIC('T', 'I', 'M', 'R')
00072 #define VALID_TIMER(t)                  ISC_MAGIC_VALID(t, TIMER_MAGIC)
00073 
00074 typedef struct isc__timer isc__timer_t;
00075 typedef struct isc__timermgr isc__timermgr_t;
00076 
00077 struct isc__timer {
00078         /*! Not locked. */
00079         isc_timer_t                     common;
00080         isc__timermgr_t *               manager;
00081         isc_mutex_t                     lock;
00082         /*! Locked by timer lock. */
00083         unsigned int                    references;
00084         isc_time_t                      idle;
00085         /*! Locked by manager lock. */
00086         isc_timertype_t                 type;
00087         isc_time_t                      expires;
00088         isc_interval_t                  interval;
00089         isc_task_t *                    task;
00090         isc_taskaction_t                action;
00091         void *                          arg;
00092         unsigned int                    index;
00093         isc_time_t                      due;
00094         LINK(isc__timer_t)              link;
00095 };
00096 
00097 #define TIMER_MANAGER_MAGIC             ISC_MAGIC('T', 'I', 'M', 'M')
00098 #define VALID_MANAGER(m)                ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
00099 
00100 struct isc__timermgr {
00101         /* Not locked. */
00102         isc_timermgr_t                  common;
00103         isc_mem_t *                     mctx;
00104         isc_mutex_t                     lock;
00105         /* Locked by manager lock. */
00106         isc_boolean_t                   done;
00107         LIST(isc__timer_t)              timers;
00108         unsigned int                    nscheduled;
00109         isc_time_t                      due;
00110 #ifdef USE_TIMER_THREAD
00111         isc_condition_t                 wakeup;
00112         isc_thread_t                    thread;
00113 #endif  /* USE_TIMER_THREAD */
00114 #ifdef USE_SHARED_MANAGER
00115         unsigned int                    refs;
00116 #endif /* USE_SHARED_MANAGER */
00117         isc_heap_t *                    heap;
00118 };
00119 
00120 /*%
00121  * The following are intended for internal use (indicated by "isc__"
00122  * prefix) but are not declared as static, allowing direct access from
00123  * unit tests etc.
00124  */
00125 
00126 isc_result_t
00127 isc__timer_create(isc_timermgr_t *manager, isc_timertype_t type,
00128                   const isc_time_t *expires, const isc_interval_t *interval,
00129                   isc_task_t *task, isc_taskaction_t action, void *arg,
00130                   isc_timer_t **timerp);
00131 isc_result_t
00132 isc__timer_reset(isc_timer_t *timer, isc_timertype_t type,
00133                  const isc_time_t *expires, const isc_interval_t *interval,
00134                  isc_boolean_t purge);
00135 isc_timertype_t
00136 isc_timer_gettype(isc_timer_t *timer);
00137 isc_result_t
00138 isc__timer_touch(isc_timer_t *timer);
00139 void
00140 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp);
00141 void
00142 isc__timer_detach(isc_timer_t **timerp);
00143 isc_result_t
00144 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
00145 void
00146 isc_timermgr_poke(isc_timermgr_t *manager0);
00147 void
00148 isc__timermgr_destroy(isc_timermgr_t **managerp);
00149 
00150 static struct isc__timermethods {
00151         isc_timermethods_t methods;
00152 
00153         /*%
00154          * The following are defined just for avoiding unused static functions.
00155          */
00156         void *gettype;
00157 } timermethods = {
00158         {
00159                 isc__timer_attach,
00160                 isc__timer_detach,
00161                 isc__timer_reset,
00162                 isc__timer_touch
00163         },
00164         (void *)isc_timer_gettype
00165 };
00166 
00167 static struct isc__timermgrmethods {
00168         isc_timermgrmethods_t methods;
00169         void *poke;             /* see above */
00170 } timermgrmethods = {
00171         {
00172                 isc__timermgr_destroy,
00173                 isc__timer_create
00174         },
00175         (void *)isc_timermgr_poke
00176 };
00177 
00178 #ifdef USE_SHARED_MANAGER
00179 /*!
00180  * If the manager is supposed to be shared, there can be only one.
00181  */
00182 static isc__timermgr_t *timermgr = NULL;
00183 #endif /* USE_SHARED_MANAGER */
00184 
00185 static inline isc_result_t
00186 schedule(isc__timer_t *timer, isc_time_t *now, isc_boolean_t signal_ok) {
00187         isc_result_t result;
00188         isc__timermgr_t *manager;
00189         isc_time_t due;
00190         int cmp;
00191 #ifdef USE_TIMER_THREAD
00192         isc_boolean_t timedwait;
00193 #endif
00194 
00195         /*!
00196          * Note: the caller must ensure locking.
00197          */
00198 
00199         REQUIRE(timer->type != isc_timertype_inactive);
00200 
00201 #ifndef USE_TIMER_THREAD
00202         UNUSED(signal_ok);
00203 #endif /* USE_TIMER_THREAD */
00204 
00205         manager = timer->manager;
00206 
00207 #ifdef USE_TIMER_THREAD
00208         /*!
00209          * If the manager was timed wait, we may need to signal the
00210          * manager to force a wakeup.
00211          */
00212         timedwait = ISC_TF(manager->nscheduled > 0 &&
00213                            isc_time_seconds(&manager->due) != 0);
00214 #endif
00215 
00216         /*
00217          * Compute the new due time.
00218          */
00219         if (timer->type != isc_timertype_once) {
00220                 result = isc_time_add(now, &timer->interval, &due);
00221                 if (result != ISC_R_SUCCESS)
00222                         return (result);
00223                 if (timer->type == isc_timertype_limited &&
00224                     isc_time_compare(&timer->expires, &due) < 0)
00225                         due = timer->expires;
00226         } else {
00227                 if (isc_time_isepoch(&timer->idle))
00228                         due = timer->expires;
00229                 else if (isc_time_isepoch(&timer->expires))
00230                         due = timer->idle;
00231                 else if (isc_time_compare(&timer->idle, &timer->expires) < 0)
00232                         due = timer->idle;
00233                 else
00234                         due = timer->expires;
00235         }
00236 
00237         /*
00238          * Schedule the timer.
00239          */
00240 
00241         if (timer->index > 0) {
00242                 /*
00243                  * Already scheduled.
00244                  */
00245                 cmp = isc_time_compare(&due, &timer->due);
00246                 timer->due = due;
00247                 switch (cmp) {
00248                 case -1:
00249                         isc_heap_increased(manager->heap, timer->index);
00250                         break;
00251                 case 1:
00252                         isc_heap_decreased(manager->heap, timer->index);
00253                         break;
00254                 case 0:
00255                         /* Nothing to do. */
00256                         break;
00257                 }
00258         } else {
00259                 timer->due = due;
00260                 result = isc_heap_insert(manager->heap, timer);
00261                 if (result != ISC_R_SUCCESS) {
00262                         INSIST(result == ISC_R_NOMEMORY);
00263                         return (ISC_R_NOMEMORY);
00264                 }
00265                 manager->nscheduled++;
00266         }
00267 
00268         XTRACETIMER(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
00269                                    ISC_MSG_SCHEDULE, "schedule"), timer, due);
00270 
00271         /*
00272          * If this timer is at the head of the queue, we need to ensure
00273          * that we won't miss it if it has a more recent due time than
00274          * the current "next" timer.  We do this either by waking up the
00275          * run thread, or explicitly setting the value in the manager.
00276          */
00277 #ifdef USE_TIMER_THREAD
00278 
00279         /*
00280          * This is a temporary (probably) hack to fix a bug on tru64 5.1
00281          * and 5.1a.  Sometimes, pthread_cond_timedwait() doesn't actually
00282          * return when the time expires, so here, we check to see if
00283          * we're 15 seconds or more behind, and if we are, we signal
00284          * the dispatcher.  This isn't such a bad idea as a general purpose
00285          * watchdog, so perhaps we should just leave it in here.
00286          */
00287         if (signal_ok && timedwait) {
00288                 isc_interval_t fifteen;
00289                 isc_time_t then;
00290 
00291                 isc_interval_set(&fifteen, 15, 0);
00292                 result = isc_time_add(&manager->due, &fifteen, &then);
00293 
00294                 if (result == ISC_R_SUCCESS &&
00295                     isc_time_compare(&then, now) < 0) {
00296                         SIGNAL(&manager->wakeup);
00297                         signal_ok = ISC_FALSE;
00298                         isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL,
00299                                       ISC_LOGMODULE_TIMER, ISC_LOG_WARNING,
00300                                       "*** POKED TIMER ***");
00301                 }
00302         }
00303 
00304         if (timer->index == 1 && signal_ok) {
00305                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
00306                                       ISC_MSG_SIGNALSCHED,
00307                                       "signal (schedule)"));
00308                 SIGNAL(&manager->wakeup);
00309         }
00310 #else /* USE_TIMER_THREAD */
00311         if (timer->index == 1 &&
00312             isc_time_compare(&timer->due, &manager->due) < 0)
00313                 manager->due = timer->due;
00314 #endif /* USE_TIMER_THREAD */
00315 
00316         return (ISC_R_SUCCESS);
00317 }
00318 
00319 static inline void
00320 deschedule(isc__timer_t *timer) {
00321 #ifdef USE_TIMER_THREAD
00322         isc_boolean_t need_wakeup = ISC_FALSE;
00323 #endif
00324         isc__timermgr_t *manager;
00325 
00326         /*
00327          * The caller must ensure locking.
00328          */
00329 
00330         manager = timer->manager;
00331         if (timer->index > 0) {
00332 #ifdef USE_TIMER_THREAD
00333                 if (timer->index == 1)
00334                         need_wakeup = ISC_TRUE;
00335 #endif
00336                 isc_heap_delete(manager->heap, timer->index);
00337                 timer->index = 0;
00338                 INSIST(manager->nscheduled > 0);
00339                 manager->nscheduled--;
00340 #ifdef USE_TIMER_THREAD
00341                 if (need_wakeup) {
00342                         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
00343                                               ISC_MSG_SIGNALDESCHED,
00344                                               "signal (deschedule)"));
00345                         SIGNAL(&manager->wakeup);
00346                 }
00347 #endif /* USE_TIMER_THREAD */
00348         }
00349 }
00350 
00351 static void
00352 destroy(isc__timer_t *timer) {
00353         isc__timermgr_t *manager = timer->manager;
00354 
00355         /*
00356          * The caller must ensure it is safe to destroy the timer.
00357          */
00358 
00359         LOCK(&manager->lock);
00360 
00361         (void)isc_task_purgerange(timer->task,
00362                                   timer,
00363                                   ISC_TIMEREVENT_FIRSTEVENT,
00364                                   ISC_TIMEREVENT_LASTEVENT,
00365                                   NULL);
00366         deschedule(timer);
00367         UNLINK(manager->timers, timer, link);
00368 
00369         UNLOCK(&manager->lock);
00370 
00371         isc_task_detach(&timer->task);
00372         DESTROYLOCK(&timer->lock);
00373         timer->common.impmagic = 0;
00374         timer->common.magic = 0;
00375         isc_mem_put(manager->mctx, timer, sizeof(*timer));
00376 }
00377 
00378 isc_result_t
00379 isc__timer_create(isc_timermgr_t *manager0, isc_timertype_t type,
00380                   const isc_time_t *expires, const isc_interval_t *interval,
00381                   isc_task_t *task, isc_taskaction_t action, void *arg,
00382                   isc_timer_t **timerp)
00383 {
00384         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
00385         isc__timer_t *timer;
00386         isc_result_t result;
00387         isc_time_t now;
00388 
00389         /*
00390          * Create a new 'type' timer managed by 'manager'.  The timers
00391          * parameters are specified by 'expires' and 'interval'.  Events
00392          * will be posted to 'task' and when dispatched 'action' will be
00393          * called with 'arg' as the arg value.  The new timer is returned
00394          * in 'timerp'.
00395          */
00396 
00397         REQUIRE(VALID_MANAGER(manager));
00398         REQUIRE(task != NULL);
00399         REQUIRE(action != NULL);
00400         if (expires == NULL)
00401                 expires = isc_time_epoch;
00402         if (interval == NULL)
00403                 interval = isc_interval_zero;
00404         REQUIRE(type == isc_timertype_inactive ||
00405                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
00406         REQUIRE(timerp != NULL && *timerp == NULL);
00407         REQUIRE(type != isc_timertype_limited ||
00408                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
00409 
00410         /*
00411          * Get current time.
00412          */
00413         if (type != isc_timertype_inactive) {
00414                 TIME_NOW(&now);
00415         } else {
00416                 /*
00417                  * We don't have to do this, but it keeps the compiler from
00418                  * complaining about "now" possibly being used without being
00419                  * set, even though it will never actually happen.
00420                  */
00421                 isc_time_settoepoch(&now);
00422         }
00423 
00424 
00425         timer = isc_mem_get(manager->mctx, sizeof(*timer));
00426         if (timer == NULL)
00427                 return (ISC_R_NOMEMORY);
00428 
00429         timer->manager = manager;
00430         timer->references = 1;
00431 
00432         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
00433                 result = isc_time_add(&now, interval, &timer->idle);
00434                 if (result != ISC_R_SUCCESS) {
00435                         isc_mem_put(manager->mctx, timer, sizeof(*timer));
00436                         return (result);
00437                 }
00438         } else
00439                 isc_time_settoepoch(&timer->idle);
00440 
00441         timer->type = type;
00442         timer->expires = *expires;
00443         timer->interval = *interval;
00444         timer->task = NULL;
00445         isc_task_attach(task, &timer->task);
00446         timer->action = action;
00447         /*
00448          * Removing the const attribute from "arg" is the best of two
00449          * evils here.  If the timer->arg member is made const, then
00450          * it affects a great many recipients of the timer event
00451          * which did not pass in an "arg" that was truly const.
00452          * Changing isc_timer_create() to not have "arg" prototyped as const,
00453          * though, can cause compilers warnings for calls that *do*
00454          * have a truly const arg.  The caller will have to carefully
00455          * keep track of whether arg started as a true const.
00456          */
00457         DE_CONST(arg, timer->arg);
00458         timer->index = 0;
00459         result = isc_mutex_init(&timer->lock);
00460         if (result != ISC_R_SUCCESS) {
00461                 isc_task_detach(&timer->task);
00462                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
00463                 return (result);
00464         }
00465         ISC_LINK_INIT(timer, link);
00466         timer->common.impmagic = TIMER_MAGIC;
00467         timer->common.magic = ISCAPI_TIMER_MAGIC;
00468         timer->common.methods = (isc_timermethods_t *)&timermethods;
00469 
00470         LOCK(&manager->lock);
00471 
00472         /*
00473          * Note we don't have to lock the timer like we normally would because
00474          * there are no external references to it yet.
00475          */
00476 
00477         if (type != isc_timertype_inactive)
00478                 result = schedule(timer, &now, ISC_TRUE);
00479         else
00480                 result = ISC_R_SUCCESS;
00481         if (result == ISC_R_SUCCESS)
00482                 APPEND(manager->timers, timer, link);
00483 
00484         UNLOCK(&manager->lock);
00485 
00486         if (result != ISC_R_SUCCESS) {
00487                 timer->common.impmagic = 0;
00488                 timer->common.magic = 0;
00489                 DESTROYLOCK(&timer->lock);
00490                 isc_task_detach(&timer->task);
00491                 isc_mem_put(manager->mctx, timer, sizeof(*timer));
00492                 return (result);
00493         }
00494 
00495         *timerp = (isc_timer_t *)timer;
00496 
00497         return (ISC_R_SUCCESS);
00498 }
00499 
00500 isc_result_t
00501 isc__timer_reset(isc_timer_t *timer0, isc_timertype_t type,
00502                  const isc_time_t *expires, const isc_interval_t *interval,
00503                  isc_boolean_t purge)
00504 {
00505         isc__timer_t *timer = (isc__timer_t *)timer0;
00506         isc_time_t now;
00507         isc__timermgr_t *manager;
00508         isc_result_t result;
00509 
00510         /*
00511          * Change the timer's type, expires, and interval values to the given
00512          * values.  If 'purge' is ISC_TRUE, any pending events from this timer
00513          * are purged from its task's event queue.
00514          */
00515 
00516         REQUIRE(VALID_TIMER(timer));
00517         manager = timer->manager;
00518         REQUIRE(VALID_MANAGER(manager));
00519 
00520         if (expires == NULL)
00521                 expires = isc_time_epoch;
00522         if (interval == NULL)
00523                 interval = isc_interval_zero;
00524         REQUIRE(type == isc_timertype_inactive ||
00525                 !(isc_time_isepoch(expires) && isc_interval_iszero(interval)));
00526         REQUIRE(type != isc_timertype_limited ||
00527                 !(isc_time_isepoch(expires) || isc_interval_iszero(interval)));
00528 
00529         /*
00530          * Get current time.
00531          */
00532         if (type != isc_timertype_inactive) {
00533                 TIME_NOW(&now);
00534         } else {
00535                 /*
00536                  * We don't have to do this, but it keeps the compiler from
00537                  * complaining about "now" possibly being used without being
00538                  * set, even though it will never actually happen.
00539                  */
00540                 isc_time_settoepoch(&now);
00541         }
00542 
00543         LOCK(&manager->lock);
00544         LOCK(&timer->lock);
00545 
00546         if (purge)
00547                 (void)isc_task_purgerange(timer->task,
00548                                           timer,
00549                                           ISC_TIMEREVENT_FIRSTEVENT,
00550                                           ISC_TIMEREVENT_LASTEVENT,
00551                                           NULL);
00552         timer->type = type;
00553         timer->expires = *expires;
00554         timer->interval = *interval;
00555         if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
00556                 result = isc_time_add(&now, interval, &timer->idle);
00557         } else {
00558                 isc_time_settoepoch(&timer->idle);
00559                 result = ISC_R_SUCCESS;
00560         }
00561 
00562         if (result == ISC_R_SUCCESS) {
00563                 if (type == isc_timertype_inactive) {
00564                         deschedule(timer);
00565                         result = ISC_R_SUCCESS;
00566                 } else
00567                         result = schedule(timer, &now, ISC_TRUE);
00568         }
00569 
00570         UNLOCK(&timer->lock);
00571         UNLOCK(&manager->lock);
00572 
00573         return (result);
00574 }
00575 
00576 isc_timertype_t
00577 isc_timer_gettype(isc_timer_t *timer0) {
00578         isc__timer_t *timer = (isc__timer_t *)timer0;
00579         isc_timertype_t t;
00580 
00581         REQUIRE(VALID_TIMER(timer));
00582 
00583         LOCK(&timer->lock);
00584         t = timer->type;
00585         UNLOCK(&timer->lock);
00586 
00587         return (t);
00588 }
00589 
00590 isc_result_t
00591 isc__timer_touch(isc_timer_t *timer0) {
00592         isc__timer_t *timer = (isc__timer_t *)timer0;
00593         isc_result_t result;
00594         isc_time_t now;
00595 
00596         /*
00597          * Set the last-touched time of 'timer' to the current time.
00598          */
00599 
00600         REQUIRE(VALID_TIMER(timer));
00601 
00602         LOCK(&timer->lock);
00603 
00604         /*
00605          * We'd like to
00606          *
00607          *      REQUIRE(timer->type == isc_timertype_once);
00608          *
00609          * but we cannot without locking the manager lock too, which we
00610          * don't want to do.
00611          */
00612 
00613         TIME_NOW(&now);
00614         result = isc_time_add(&now, &timer->interval, &timer->idle);
00615 
00616         UNLOCK(&timer->lock);
00617 
00618         return (result);
00619 }
00620 
00621 void
00622 isc__timer_attach(isc_timer_t *timer0, isc_timer_t **timerp) {
00623         isc__timer_t *timer = (isc__timer_t *)timer0;
00624 
00625         /*
00626          * Attach *timerp to timer.
00627          */
00628 
00629         REQUIRE(VALID_TIMER(timer));
00630         REQUIRE(timerp != NULL && *timerp == NULL);
00631 
00632         LOCK(&timer->lock);
00633         timer->references++;
00634         UNLOCK(&timer->lock);
00635 
00636         *timerp = (isc_timer_t *)timer;
00637 }
00638 
00639 void
00640 isc__timer_detach(isc_timer_t **timerp) {
00641         isc__timer_t *timer;
00642         isc_boolean_t free_timer = ISC_FALSE;
00643 
00644         /*
00645          * Detach *timerp from its timer.
00646          */
00647 
00648         REQUIRE(timerp != NULL);
00649         timer = (isc__timer_t *)*timerp;
00650         REQUIRE(VALID_TIMER(timer));
00651 
00652         LOCK(&timer->lock);
00653         REQUIRE(timer->references > 0);
00654         timer->references--;
00655         if (timer->references == 0)
00656                 free_timer = ISC_TRUE;
00657         UNLOCK(&timer->lock);
00658 
00659         if (free_timer)
00660                 destroy(timer);
00661 
00662         *timerp = NULL;
00663 }
00664 
00665 static void
00666 dispatch(isc__timermgr_t *manager, isc_time_t *now) {
00667         isc_boolean_t done = ISC_FALSE, post_event, need_schedule;
00668         isc_timerevent_t *event;
00669         isc_eventtype_t type = 0;
00670         isc__timer_t *timer;
00671         isc_result_t result;
00672         isc_boolean_t idle;
00673 
00674         /*!
00675          * The caller must be holding the manager lock.
00676          */
00677 
00678         while (manager->nscheduled > 0 && !done) {
00679                 timer = isc_heap_element(manager->heap, 1);
00680                 INSIST(timer != NULL && timer->type != isc_timertype_inactive);
00681                 if (isc_time_compare(now, &timer->due) >= 0) {
00682                         if (timer->type == isc_timertype_ticker) {
00683                                 type = ISC_TIMEREVENT_TICK;
00684                                 post_event = ISC_TRUE;
00685                                 need_schedule = ISC_TRUE;
00686                         } else if (timer->type == isc_timertype_limited) {
00687                                 int cmp;
00688                                 cmp = isc_time_compare(now, &timer->expires);
00689                                 if (cmp >= 0) {
00690                                         type = ISC_TIMEREVENT_LIFE;
00691                                         post_event = ISC_TRUE;
00692                                         need_schedule = ISC_FALSE;
00693                                 } else {
00694                                         type = ISC_TIMEREVENT_TICK;
00695                                         post_event = ISC_TRUE;
00696                                         need_schedule = ISC_TRUE;
00697                                 }
00698                         } else if (!isc_time_isepoch(&timer->expires) &&
00699                                    isc_time_compare(now,
00700                                                     &timer->expires) >= 0) {
00701                                 type = ISC_TIMEREVENT_LIFE;
00702                                 post_event = ISC_TRUE;
00703                                 need_schedule = ISC_FALSE;
00704                         } else {
00705                                 idle = ISC_FALSE;
00706 
00707                                 LOCK(&timer->lock);
00708                                 if (!isc_time_isepoch(&timer->idle) &&
00709                                     isc_time_compare(now,
00710                                                      &timer->idle) >= 0) {
00711                                         idle = ISC_TRUE;
00712                                 }
00713                                 UNLOCK(&timer->lock);
00714                                 if (idle) {
00715                                         type = ISC_TIMEREVENT_IDLE;
00716                                         post_event = ISC_TRUE;
00717                                         need_schedule = ISC_FALSE;
00718                                 } else {
00719                                         /*
00720                                          * Idle timer has been touched;
00721                                          * reschedule.
00722                                          */
00723                                         XTRACEID(isc_msgcat_get(isc_msgcat,
00724                                                                 ISC_MSGSET_TIMER,
00725                                                                 ISC_MSG_IDLERESCHED,
00726                                                                 "idle reschedule"),
00727                                                  timer);
00728                                         post_event = ISC_FALSE;
00729                                         need_schedule = ISC_TRUE;
00730                                 }
00731                         }
00732 
00733                         if (post_event) {
00734                                 XTRACEID(isc_msgcat_get(isc_msgcat,
00735                                                         ISC_MSGSET_TIMER,
00736                                                         ISC_MSG_POSTING,
00737                                                         "posting"), timer);
00738                                 /*
00739                                  * XXX We could preallocate this event.
00740                                  */
00741                                 event = (isc_timerevent_t *)isc_event_allocate(manager->mctx,
00742                                                            timer,
00743                                                            type,
00744                                                            timer->action,
00745                                                            timer->arg,
00746                                                            sizeof(*event));
00747 
00748                                 if (event != NULL) {
00749                                         event->due = timer->due;
00750                                         isc_task_send(timer->task,
00751                                                       ISC_EVENT_PTR(&event));
00752                                 } else
00753                                         UNEXPECTED_ERROR(__FILE__, __LINE__, "%s",
00754                                                  isc_msgcat_get(isc_msgcat,
00755                                                          ISC_MSGSET_TIMER,
00756                                                          ISC_MSG_EVENTNOTALLOC,
00757                                                          "couldn't "
00758                                                          "allocate event"));
00759                         }
00760 
00761                         timer->index = 0;
00762                         isc_heap_delete(manager->heap, 1);
00763                         manager->nscheduled--;
00764 
00765                         if (need_schedule) {
00766                                 result = schedule(timer, now, ISC_FALSE);
00767                                 if (result != ISC_R_SUCCESS)
00768                                         UNEXPECTED_ERROR(__FILE__, __LINE__,
00769                                                          "%s: %u",
00770                                                 isc_msgcat_get(isc_msgcat,
00771                                                         ISC_MSGSET_TIMER,
00772                                                         ISC_MSG_SCHEDFAIL,
00773                                                         "couldn't schedule "
00774                                                         "timer"),
00775                                                          result);
00776                         }
00777                 } else {
00778                         manager->due = timer->due;
00779                         done = ISC_TRUE;
00780                 }
00781         }
00782 }
00783 
00784 #ifdef USE_TIMER_THREAD
00785 static isc_threadresult_t
00786 #ifdef _WIN32                   /* XXXDCL */
00787 WINAPI
00788 #endif
00789 run(void *uap) {
00790         isc__timermgr_t *manager = uap;
00791         isc_time_t now;
00792         isc_result_t result;
00793 
00794         LOCK(&manager->lock);
00795         while (!manager->done) {
00796                 TIME_NOW(&now);
00797 
00798                 XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00799                                           ISC_MSG_RUNNING,
00800                                           "running"), now);
00801 
00802                 dispatch(manager, &now);
00803 
00804                 if (manager->nscheduled > 0) {
00805                         XTRACETIME2(isc_msgcat_get(isc_msgcat,
00806                                                    ISC_MSGSET_GENERAL,
00807                                                    ISC_MSG_WAITUNTIL,
00808                                                    "waituntil"),
00809                                     manager->due, now);
00810                         result = WAITUNTIL(&manager->wakeup, &manager->lock, &manager->due);
00811                         INSIST(result == ISC_R_SUCCESS ||
00812                                result == ISC_R_TIMEDOUT);
00813                 } else {
00814                         XTRACETIME(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00815                                                   ISC_MSG_WAIT, "wait"), now);
00816                         WAIT(&manager->wakeup, &manager->lock);
00817                 }
00818                 XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
00819                                       ISC_MSG_WAKEUP, "wakeup"));
00820         }
00821         UNLOCK(&manager->lock);
00822 
00823 #ifdef OPENSSL_LEAKS
00824         ERR_remove_state(0);
00825 #endif
00826 
00827         return ((isc_threadresult_t)0);
00828 }
00829 #endif /* USE_TIMER_THREAD */
00830 
00831 static isc_boolean_t
00832 sooner(void *v1, void *v2) {
00833         isc__timer_t *t1, *t2;
00834 
00835         t1 = v1;
00836         t2 = v2;
00837         REQUIRE(VALID_TIMER(t1));
00838         REQUIRE(VALID_TIMER(t2));
00839 
00840         if (isc_time_compare(&t1->due, &t2->due) < 0)
00841                 return (ISC_TRUE);
00842         return (ISC_FALSE);
00843 }
00844 
00845 static void
00846 set_index(void *what, unsigned int index) {
00847         isc__timer_t *timer;
00848 
00849         timer = what;
00850         REQUIRE(VALID_TIMER(timer));
00851 
00852         timer->index = index;
00853 }
00854 
00855 isc_result_t
00856 isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
00857         isc__timermgr_t *manager;
00858         isc_result_t result;
00859 
00860         /*
00861          * Create a timer manager.
00862          */
00863 
00864         REQUIRE(managerp != NULL && *managerp == NULL);
00865 
00866 #ifdef USE_SHARED_MANAGER
00867         if (timermgr != NULL) {
00868                 timermgr->refs++;
00869                 *managerp = (isc_timermgr_t *)timermgr;
00870                 return (ISC_R_SUCCESS);
00871         }
00872 #endif /* USE_SHARED_MANAGER */
00873 
00874         manager = isc_mem_get(mctx, sizeof(*manager));
00875         if (manager == NULL)
00876                 return (ISC_R_NOMEMORY);
00877 
00878         manager->common.impmagic = TIMER_MANAGER_MAGIC;
00879         manager->common.magic = ISCAPI_TIMERMGR_MAGIC;
00880         manager->common.methods = (isc_timermgrmethods_t *)&timermgrmethods;
00881         manager->mctx = NULL;
00882         manager->done = ISC_FALSE;
00883         INIT_LIST(manager->timers);
00884         manager->nscheduled = 0;
00885         isc_time_settoepoch(&manager->due);
00886         manager->heap = NULL;
00887         result = isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
00888         if (result != ISC_R_SUCCESS) {
00889                 INSIST(result == ISC_R_NOMEMORY);
00890                 isc_mem_put(mctx, manager, sizeof(*manager));
00891                 return (ISC_R_NOMEMORY);
00892         }
00893         result = isc_mutex_init(&manager->lock);
00894         if (result != ISC_R_SUCCESS) {
00895                 isc_heap_destroy(&manager->heap);
00896                 isc_mem_put(mctx, manager, sizeof(*manager));
00897                 return (result);
00898         }
00899         isc_mem_attach(mctx, &manager->mctx);
00900 #ifdef USE_TIMER_THREAD
00901         if (isc_condition_init(&manager->wakeup) != ISC_R_SUCCESS) {
00902                 isc_mem_detach(&manager->mctx);
00903                 DESTROYLOCK(&manager->lock);
00904                 isc_heap_destroy(&manager->heap);
00905                 isc_mem_put(mctx, manager, sizeof(*manager));
00906                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00907                                  "isc_condition_init() %s",
00908                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00909                                                 ISC_MSG_FAILED, "failed"));
00910                 return (ISC_R_UNEXPECTED);
00911         }
00912         if (isc_thread_create(run, manager, &manager->thread) !=
00913             ISC_R_SUCCESS) {
00914                 isc_mem_detach(&manager->mctx);
00915                 (void)isc_condition_destroy(&manager->wakeup);
00916                 DESTROYLOCK(&manager->lock);
00917                 isc_heap_destroy(&manager->heap);
00918                 isc_mem_put(mctx, manager, sizeof(*manager));
00919                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00920                                  "isc_thread_create() %s",
00921                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00922                                                 ISC_MSG_FAILED, "failed"));
00923                 return (ISC_R_UNEXPECTED);
00924         }
00925 #endif
00926 #ifdef USE_SHARED_MANAGER
00927         manager->refs = 1;
00928         timermgr = manager;
00929 #endif /* USE_SHARED_MANAGER */
00930 
00931         *managerp = (isc_timermgr_t *)manager;
00932 
00933         return (ISC_R_SUCCESS);
00934 }
00935 
00936 void
00937 isc_timermgr_poke(isc_timermgr_t *manager0) {
00938 #ifdef USE_TIMER_THREAD
00939         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
00940 
00941         REQUIRE(VALID_MANAGER(manager));
00942 
00943         SIGNAL(&manager->wakeup);
00944 #else
00945         UNUSED(manager0);
00946 #endif
00947 }
00948 
00949 void
00950 isc__timermgr_destroy(isc_timermgr_t **managerp) {
00951         isc__timermgr_t *manager;
00952         isc_mem_t *mctx;
00953 
00954         /*
00955          * Destroy a timer manager.
00956          */
00957 
00958         REQUIRE(managerp != NULL);
00959         manager = (isc__timermgr_t *)*managerp;
00960         REQUIRE(VALID_MANAGER(manager));
00961 
00962         LOCK(&manager->lock);
00963 
00964 #ifdef USE_SHARED_MANAGER
00965         manager->refs--;
00966         if (manager->refs > 0) {
00967                 UNLOCK(&manager->lock);
00968                 *managerp = NULL;
00969                 return;
00970         }
00971         timermgr = NULL;
00972 #endif /* USE_SHARED_MANAGER */
00973 
00974 #ifndef USE_TIMER_THREAD
00975         isc__timermgr_dispatch((isc_timermgr_t *)manager);
00976 #endif
00977 
00978         REQUIRE(EMPTY(manager->timers));
00979         manager->done = ISC_TRUE;
00980 
00981 #ifdef USE_TIMER_THREAD
00982         XTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TIMER,
00983                               ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
00984         SIGNAL(&manager->wakeup);
00985 #endif /* USE_TIMER_THREAD */
00986 
00987         UNLOCK(&manager->lock);
00988 
00989 #ifdef USE_TIMER_THREAD
00990         /*
00991          * Wait for thread to exit.
00992          */
00993         if (isc_thread_join(manager->thread, NULL) != ISC_R_SUCCESS)
00994                 UNEXPECTED_ERROR(__FILE__, __LINE__,
00995                                  "isc_thread_join() %s",
00996                                  isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL,
00997                                                 ISC_MSG_FAILED, "failed"));
00998 #endif /* USE_TIMER_THREAD */
00999 
01000         /*
01001          * Clean up.
01002          */
01003 #ifdef USE_TIMER_THREAD
01004         (void)isc_condition_destroy(&manager->wakeup);
01005 #endif /* USE_TIMER_THREAD */
01006         DESTROYLOCK(&manager->lock);
01007         isc_heap_destroy(&manager->heap);
01008         manager->common.impmagic = 0;
01009         manager->common.magic = 0;
01010         mctx = manager->mctx;
01011         isc_mem_put(mctx, manager, sizeof(*manager));
01012         isc_mem_detach(&mctx);
01013 
01014         *managerp = NULL;
01015 
01016 #ifdef USE_SHARED_MANAGER
01017         timermgr = NULL;
01018 #endif
01019 }
01020 
01021 #ifndef USE_TIMER_THREAD
01022 isc_result_t
01023 isc__timermgr_nextevent(isc_timermgr_t *manager0, isc_time_t *when) {
01024         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
01025 
01026 #ifdef USE_SHARED_MANAGER
01027         if (manager == NULL)
01028                 manager = timermgr;
01029 #endif
01030         if (manager == NULL || manager->nscheduled == 0)
01031                 return (ISC_R_NOTFOUND);
01032         *when = manager->due;
01033         return (ISC_R_SUCCESS);
01034 }
01035 
01036 void
01037 isc__timermgr_dispatch(isc_timermgr_t *manager0) {
01038         isc__timermgr_t *manager = (isc__timermgr_t *)manager0;
01039         isc_time_t now;
01040 
01041 #ifdef USE_SHARED_MANAGER
01042         if (manager == NULL)
01043                 manager = timermgr;
01044 #endif
01045         if (manager == NULL)
01046                 return;
01047         TIME_NOW(&now);
01048         dispatch(manager, &now);
01049 }
01050 #endif /* USE_TIMER_THREAD */
01051 
01052 isc_result_t
01053 isc__timer_register(void) {
01054         return (isc_timer_register(isc__timermgr_create));
01055 }
01056 
01057 static isc_mutex_t createlock;
01058 static isc_once_t once = ISC_ONCE_INIT;
01059 static isc_timermgrcreatefunc_t timermgr_createfunc = NULL;
01060 
01061 static void
01062 initialize(void) {
01063         RUNTIME_CHECK(isc_mutex_init(&createlock) == ISC_R_SUCCESS);
01064 }
01065 
01066 isc_result_t
01067 isc_timer_register(isc_timermgrcreatefunc_t createfunc) {
01068         isc_result_t result = ISC_R_SUCCESS;
01069 
01070         RUNTIME_CHECK(isc_once_do(&once, initialize) == ISC_R_SUCCESS);
01071 
01072         LOCK(&createlock);
01073         if (timermgr_createfunc == NULL)
01074                 timermgr_createfunc = createfunc;
01075         else
01076                 result = ISC_R_EXISTS;
01077         UNLOCK(&createlock);
01078 
01079         return (result);
01080 }
01081 
01082 isc_result_t
01083 isc_timermgr_createinctx(isc_mem_t *mctx, isc_appctx_t *actx,
01084                          isc_timermgr_t **managerp)
01085 {
01086         isc_result_t result;
01087 
01088         LOCK(&createlock);
01089 
01090         REQUIRE(timermgr_createfunc != NULL);
01091         result = (*timermgr_createfunc)(mctx, managerp);
01092 
01093         UNLOCK(&createlock);
01094 
01095         if (result == ISC_R_SUCCESS)
01096                 isc_appctx_settimermgr(actx, *managerp);
01097 
01098         return (result);
01099 }
01100 
01101 isc_result_t
01102 isc_timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
01103         isc_result_t result;
01104 
01105         if (isc_bind9)
01106                 return (isc__timermgr_create(mctx, managerp));
01107 
01108         LOCK(&createlock);
01109 
01110         REQUIRE(timermgr_createfunc != NULL);
01111         result = (*timermgr_createfunc)(mctx, managerp);
01112 
01113         UNLOCK(&createlock);
01114 
01115         return (result);
01116 }
01117 
01118 void
01119 isc_timermgr_destroy(isc_timermgr_t **managerp) {
01120         REQUIRE(*managerp != NULL && ISCAPI_TIMERMGR_VALID(*managerp));
01121 
01122         if (isc_bind9)
01123                 isc__timermgr_destroy(managerp);
01124         else
01125                 (*managerp)->methods->destroy(managerp);
01126 
01127         ENSURE(*managerp == NULL);
01128 }
01129 
01130 isc_result_t
01131 isc_timer_create(isc_timermgr_t *manager, isc_timertype_t type,
01132                  const isc_time_t *expires, const isc_interval_t *interval,
01133                  isc_task_t *task, isc_taskaction_t action, void *arg,
01134                  isc_timer_t **timerp)
01135 {
01136         REQUIRE(ISCAPI_TIMERMGR_VALID(manager));
01137 
01138         if (isc_bind9)
01139                 return (isc__timer_create(manager, type, expires, interval,
01140                                           task, action, arg, timerp));
01141 
01142         return (manager->methods->timercreate(manager, type, expires,
01143                                               interval, task, action, arg,
01144                                               timerp));
01145 }
01146 
01147 void
01148 isc_timer_attach(isc_timer_t *timer, isc_timer_t **timerp) {
01149         REQUIRE(ISCAPI_TIMER_VALID(timer));
01150         REQUIRE(timerp != NULL && *timerp == NULL);
01151 
01152         if (isc_bind9)
01153                 isc__timer_attach(timer, timerp);
01154         else
01155                 timer->methods->attach(timer, timerp);
01156 
01157         ENSURE(*timerp == timer);
01158 }
01159 
01160 void
01161 isc_timer_detach(isc_timer_t **timerp) {
01162         REQUIRE(timerp != NULL && ISCAPI_TIMER_VALID(*timerp));
01163 
01164         if (isc_bind9)
01165                 isc__timer_detach(timerp);
01166         else
01167                 (*timerp)->methods->detach(timerp);
01168 
01169         ENSURE(*timerp == NULL);
01170 }
01171 
01172 isc_result_t
01173 isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
01174                 const isc_time_t *expires, const isc_interval_t *interval,
01175                 isc_boolean_t purge)
01176 {
01177         REQUIRE(ISCAPI_TIMER_VALID(timer));
01178 
01179         if (isc_bind9)
01180                 return (isc__timer_reset(timer, type, expires,
01181                                          interval, purge));
01182 
01183         return (timer->methods->reset(timer, type, expires, interval, purge));
01184 }
01185 
01186 isc_result_t
01187 isc_timer_touch(isc_timer_t *timer) {
01188         REQUIRE(ISCAPI_TIMER_VALID(timer));
01189 
01190         if (isc_bind9)
01191                 return (isc__timer_touch(timer));
01192 
01193         return (timer->methods->touch(timer));
01194 }

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