timer.c revision 0c27b3fe77ac1d5094ba3521e8142d9e7973133f
/*
* Copyright (C) 1998-2002, 2004, 2005, 2007-2009, 2011-2016 Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* $Id$ */
/*! \file */
#include <config.h>
#include <isc/condition.h>
#include <isc/platform.h>
#ifdef OPENSSL_LEAKS
#endif
/* See task.c about the following definition: */
#ifdef ISC_PLATFORM_USETHREADS
#define USE_TIMER_THREAD
#else
#define USE_SHARED_MANAGER
#endif /* ISC_PLATFORM_USETHREADS */
#ifndef USE_TIMER_THREAD
#include "timer_p.h"
#endif /* USE_TIMER_THREAD */
#ifdef ISC_TIMER_TRACE
(d).seconds, (d).nanoseconds)
(d).seconds, (d).nanoseconds)
#else
#define XTRACE(s)
#define XTRACEID(s, t)
#define XTRACETIME(s, d)
#define XTRACETIME2(s, d, n)
#define XTRACETIMER(s, t, d)
#endif /* ISC_TIMER_TRACE */
typedef struct isc__timer isc__timer_t;
typedef struct isc__timermgr isc__timermgr_t;
struct isc__timer {
/*! Not locked. */
/*! Locked by timer lock. */
unsigned int references;
/*! Locked by manager lock. */
isc_task_t * task;
void * arg;
unsigned int index;
};
struct isc__timermgr {
/* Not locked. */
/* Locked by manager lock. */
unsigned int nscheduled;
#ifdef USE_TIMER_THREAD
#endif /* USE_TIMER_THREAD */
#ifdef USE_SHARED_MANAGER
unsigned int refs;
#endif /* USE_SHARED_MANAGER */
isc_heap_t * heap;
};
/*%
* The following are intended for internal use (indicated by "isc__"
* prefix) but are not declared as static, allowing direct access from
* unit tests etc.
*/
isc_timer_t **timerp);
void
void
void
void
static struct isc__timermethods {
/*%
* The following are defined just for avoiding unused static functions.
*/
void *gettype;
} timermethods = {
{
},
(void *)isc_timer_gettype
};
static struct isc__timermgrmethods {
void *poke; /* see above */
} timermgrmethods = {
{
},
(void *)isc_timermgr_poke
};
#ifdef USE_SHARED_MANAGER
/*!
* If the manager is supposed to be shared, there can be only one.
*/
#endif /* USE_SHARED_MANAGER */
static inline isc_result_t
int cmp;
#ifdef USE_TIMER_THREAD
#endif
/*!
* Note: the caller must ensure locking.
*/
#ifndef USE_TIMER_THREAD
#endif /* USE_TIMER_THREAD */
#ifdef USE_TIMER_THREAD
/*!
* If the manager was timed wait, we may need to signal the
* manager to force a wakeup.
*/
#endif
/*
* Compute the new due time.
*/
if (result != ISC_R_SUCCESS)
return (result);
} else {
else
}
/*
* Schedule the timer.
*/
/*
* Already scheduled.
*/
switch (cmp) {
case -1:
break;
case 1:
break;
case 0:
/* Nothing to do. */
break;
}
} else {
if (result != ISC_R_SUCCESS) {
return (ISC_R_NOMEMORY);
}
manager->nscheduled++;
}
/*
* If this timer is at the head of the queue, we need to ensure
* that we won't miss it if it has a more recent due time than
* the current "next" timer. We do this either by waking up the
* run thread, or explicitly setting the value in the manager.
*/
#ifdef USE_TIMER_THREAD
/*
* This is a temporary (probably) hack to fix a bug on tru64 5.1
* and 5.1a. Sometimes, pthread_cond_timedwait() doesn't actually
* return when the time expires, so here, we check to see if
* we're 15 seconds or more behind, and if we are, we signal
* the dispatcher. This isn't such a bad idea as a general purpose
* watchdog, so perhaps we should just leave it in here.
*/
if (result == ISC_R_SUCCESS &&
"*** POKED TIMER ***");
}
}
"signal (schedule)"));
}
#else /* USE_TIMER_THREAD */
#endif /* USE_TIMER_THREAD */
return (ISC_R_SUCCESS);
}
static inline void
#ifdef USE_TIMER_THREAD
#endif
/*
* The caller must ensure locking.
*/
#ifdef USE_TIMER_THREAD
#endif
manager->nscheduled--;
#ifdef USE_TIMER_THREAD
if (need_wakeup) {
"signal (deschedule)"));
}
#endif /* USE_TIMER_THREAD */
}
}
static void
/*
* The caller must ensure it is safe to destroy the timer.
*/
NULL);
}
{
/*
* Create a new 'type' timer managed by 'manager'. The timers
* parameters are specified by 'expires' and 'interval'. Events
* will be posted to 'task' and when dispatched 'action' will be
* called with 'arg' as the arg value. The new timer is returned
* in 'timerp'.
*/
/*
* Get current time.
*/
if (type != isc_timertype_inactive) {
} else {
/*
* We don't have to do this, but it keeps the compiler from
* complaining about "now" possibly being used without being
* set, even though it will never actually happen.
*/
}
return (ISC_R_NOMEMORY);
if (result != ISC_R_SUCCESS) {
return (result);
}
} else
/*
* Removing the const attribute from "arg" is the best of two
* evils here. If the timer->arg member is made const, then
* it affects a great many recipients of the timer event
* which did not pass in an "arg" that was truly const.
* Changing isc_timer_create() to not have "arg" prototyped as const,
* though, can cause compilers warnings for calls that *do*
* have a truly const arg. The caller will have to carefully
* keep track of whether arg started as a true const.
*/
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* Note we don't have to lock the timer like we normally would because
* there are no external references to it yet.
*/
if (type != isc_timertype_inactive)
else
if (result == ISC_R_SUCCESS)
if (result != ISC_R_SUCCESS) {
return (result);
}
return (ISC_R_SUCCESS);
}
{
/*
* Change the timer's type, expires, and interval values to the given
* values. If 'purge' is ISC_TRUE, any pending events from this timer
* are purged from its task's event queue.
*/
/*
* Get current time.
*/
if (type != isc_timertype_inactive) {
} else {
/*
* We don't have to do this, but it keeps the compiler from
* complaining about "now" possibly being used without being
* set, even though it will never actually happen.
*/
}
if (purge)
NULL);
} else {
}
if (result == ISC_R_SUCCESS) {
if (type == isc_timertype_inactive) {
} else
}
return (result);
}
return (t);
}
/*
* Set the last-touched time of 'timer' to the current time.
*/
/*
* We'd like to
*
* REQUIRE(timer->type == isc_timertype_once);
*
* but we cannot without locking the manager lock too, which we
* don't want to do.
*/
return (result);
}
void
/*
* Attach *timerp to timer.
*/
timer->references++;
}
void
/*
* Detach *timerp from its timer.
*/
timer->references--;
if (timer->references == 0)
if (free_timer)
}
static void
isc_eventtype_t type = 0;
/*!
* The caller must be holding the manager lock.
*/
int cmp;
if (cmp >= 0) {
} else {
}
} else {
}
if (idle) {
} else {
/*
* Idle timer has been touched;
* reschedule.
*/
"idle reschedule"),
timer);
}
}
if (post_event) {
"posting"), timer);
/*
* XXX We could preallocate this event.
*/
type,
sizeof(*event));
ISC_EVENT_PTR(&event));
} else
"couldn't "
"allocate event"));
}
manager->nscheduled--;
if (need_schedule) {
if (result != ISC_R_SUCCESS)
"%s: %u",
"couldn't schedule "
"timer"),
result);
}
} else {
}
}
}
#ifdef USE_TIMER_THREAD
static isc_threadresult_t
#ifdef _WIN32 /* XXXDCL */
#endif
"running"), now);
if (manager->nscheduled > 0) {
"waituntil"),
result == ISC_R_TIMEDOUT);
} else {
}
ISC_MSG_WAKEUP, "wakeup"));
}
#ifdef OPENSSL_LEAKS
ERR_remove_state(0);
#endif
return ((isc_threadresult_t)0);
}
#endif /* USE_TIMER_THREAD */
static isc_boolean_t
return (ISC_TRUE);
return (ISC_FALSE);
}
static void
}
/*
* Create a timer manager.
*/
#ifdef USE_SHARED_MANAGER
return (ISC_R_SUCCESS);
}
#endif /* USE_SHARED_MANAGER */
return (ISC_R_NOMEMORY);
manager->nscheduled = 0;
if (result != ISC_R_SUCCESS) {
return (ISC_R_NOMEMORY);
}
if (result != ISC_R_SUCCESS) {
return (result);
}
#ifdef USE_TIMER_THREAD
"isc_condition_init() %s",
ISC_MSG_FAILED, "failed"));
return (ISC_R_UNEXPECTED);
}
"isc_thread_create() %s",
ISC_MSG_FAILED, "failed"));
return (ISC_R_UNEXPECTED);
}
#endif
#ifdef USE_SHARED_MANAGER
#endif /* USE_SHARED_MANAGER */
return (ISC_R_SUCCESS);
}
void
#ifdef USE_TIMER_THREAD
#else
#endif
}
void
/*
* Destroy a timer manager.
*/
#ifdef USE_SHARED_MANAGER
return;
}
#endif /* USE_SHARED_MANAGER */
#ifndef USE_TIMER_THREAD
#endif
#ifdef USE_TIMER_THREAD
ISC_MSG_SIGNALDESTROY, "signal (destroy)"));
#endif /* USE_TIMER_THREAD */
#ifdef USE_TIMER_THREAD
/*
* Wait for thread to exit.
*/
"isc_thread_join() %s",
ISC_MSG_FAILED, "failed"));
#endif /* USE_TIMER_THREAD */
/*
* Clean up.
*/
#ifdef USE_TIMER_THREAD
#endif /* USE_TIMER_THREAD */
#ifdef USE_SHARED_MANAGER
#endif
}
#ifndef USE_TIMER_THREAD
#ifdef USE_SHARED_MANAGER
#endif
return (ISC_R_NOTFOUND);
return (ISC_R_SUCCESS);
}
void
#ifdef USE_SHARED_MANAGER
#endif
return;
}
#endif /* USE_TIMER_THREAD */
isc__timer_register(void) {
return (isc_timer_register(isc__timermgr_create));
}
static isc_mutex_t createlock;
static void
initialize(void) {
}
LOCK(&createlock);
if (timermgr_createfunc == NULL)
else
UNLOCK(&createlock);
return (result);
}
{
LOCK(&createlock);
UNLOCK(&createlock);
if (result == ISC_R_SUCCESS)
return (result);
}
if (isc_bind9)
LOCK(&createlock);
UNLOCK(&createlock);
return (result);
}
void
if (isc_bind9)
else
}
{
if (isc_bind9)
timerp));
}
void
if (isc_bind9)
else
}
void
if (isc_bind9)
else
}
{
if (isc_bind9)
}
if (isc_bind9)
return (isc__timer_touch(timer));
}