Update isc_timer to use isc_loopmgr
* isc_timer was rewritten using the uv_timer, and isc_timermgr_t was completely removed; isc_timer objects are now directly created on the isc_loop event loops. * the isc_timer API has been simplified. the "inactive" timer type has been removed; timers are now stopped by calling isc_timer_stop() instead of resetting to inactive. * isc_manager now creates a loop manager rather than a timer manager. * modules and applications using isc_timer have been updated to use the new API.
This commit is contained in:
@@ -169,7 +169,6 @@ libisc_la_SOURCES = \
|
||||
mutexblock.c \
|
||||
net.c \
|
||||
netaddr.c \
|
||||
netmgr_p.h \
|
||||
netscope.c \
|
||||
nonce.c \
|
||||
openssl_shim.c \
|
||||
@@ -199,12 +198,10 @@ libisc_la_SOURCES = \
|
||||
symtab.c \
|
||||
syslog.c \
|
||||
task.c \
|
||||
task_p.h \
|
||||
thread.c \
|
||||
tid.c \
|
||||
time.c \
|
||||
timer.c \
|
||||
timer_p.h \
|
||||
tls.c \
|
||||
tls_p.h \
|
||||
tm.c \
|
||||
|
||||
@@ -22,9 +22,9 @@ typedef struct isc_managers isc_managers_t;
|
||||
|
||||
isc_result_t
|
||||
isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
|
||||
isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
|
||||
isc_timermgr_t **timermgrp);
|
||||
isc_loopmgr_t **loopmgrp, isc_nm_t **netmgrp,
|
||||
isc_taskmgr_t **taskmgrp);
|
||||
|
||||
void
|
||||
isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
|
||||
isc_timermgr_t **timermgrp);
|
||||
isc_managers_destroy(isc_loopmgr_t **loopmgr, isc_nm_t **netmgrp,
|
||||
isc_taskmgr_t **taskmgrp);
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/lang.h>
|
||||
#include <isc/loop.h>
|
||||
#include <isc/time.h>
|
||||
#include <isc/types.h>
|
||||
|
||||
@@ -41,13 +42,12 @@ ISC_LANG_BEGINDECLS
|
||||
*****/
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
|
||||
isc_task_t *task, isc_ratelimiter_t **ratelimiterp);
|
||||
isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **ratelimiterp);
|
||||
/*%<
|
||||
* Create a rate limiter. The execution interval is initially undefined.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
void
|
||||
isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval);
|
||||
/*!<
|
||||
* Set the minimum interval between event executions.
|
||||
@@ -110,37 +110,19 @@ isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter);
|
||||
*
|
||||
* Ensures:
|
||||
*\li All events that have not yet been
|
||||
* dispatched to the task are dispatched immediately with
|
||||
* dispatched to the task are dispatched immediately with
|
||||
* the #ISC_EVENTATTR_CANCELED bit set in ev_attributes.
|
||||
*
|
||||
*\li Further attempts to enqueue events will fail with
|
||||
* #ISC_R_SHUTTINGDOWN.
|
||||
* #ISC_R_SHUTTINGDOWN.
|
||||
*
|
||||
*\li The rate limiter is no longer attached to its task.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target);
|
||||
isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp);
|
||||
/*%<
|
||||
* Attach to a rate limiter.
|
||||
*/
|
||||
|
||||
void
|
||||
isc_ratelimiter_detach(isc_ratelimiter_t **ratelimiterp);
|
||||
/*%<
|
||||
* Detach from a rate limiter.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_stall(isc_ratelimiter_t *rl);
|
||||
/*%<
|
||||
* Stall event processing.
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_release(isc_ratelimiter_t *rl);
|
||||
/*%<
|
||||
* Release a stalled rate limiter.
|
||||
* Destroy the rate limiter.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
@@ -20,19 +20,13 @@
|
||||
/*! \file isc/timer.h
|
||||
* \brief Provides timers which are event sources in the task system.
|
||||
*
|
||||
* Three types of timers are supported:
|
||||
* Two types of timers are supported:
|
||||
*
|
||||
*\li 'ticker' timers generate a periodic tick event.
|
||||
*
|
||||
*\li 'once' timers generate an timeout event if the time reaches
|
||||
* the set interval.
|
||||
*
|
||||
*\li 'inactive' timers generate no events.
|
||||
*
|
||||
* Timers can change type. It is typical to create a timer as
|
||||
* an 'inactive' timer and then change it into a 'ticker' or
|
||||
* 'once' timer.
|
||||
*
|
||||
*\li MP:
|
||||
* The module ensures appropriate synchronization of data structures it
|
||||
* creates and manipulates.
|
||||
@@ -61,8 +55,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/event.h>
|
||||
#include <isc/eventclass.h>
|
||||
#include <isc/job.h>
|
||||
#include <isc/lang.h>
|
||||
#include <isc/time.h>
|
||||
#include <isc/types.h>
|
||||
@@ -78,20 +71,8 @@ typedef enum {
|
||||
isc_timertype_undefined = -1, /*%< Undefined */
|
||||
isc_timertype_ticker = 0, /*%< Ticker */
|
||||
isc_timertype_once = 1, /*%< Once */
|
||||
isc_timertype_inactive = 3 /*%< Inactive */
|
||||
} isc_timertype_t;
|
||||
|
||||
typedef struct isc_timerevent isc_timerevent_t;
|
||||
|
||||
struct isc_timerevent {
|
||||
struct isc_event common;
|
||||
isc_time_t due;
|
||||
ISC_LINK(isc_timerevent_t) ev_timerlink;
|
||||
};
|
||||
|
||||
#define ISC_TIMEREVENT_TICK (ISC_EVENTCLASS_TIMER + 0)
|
||||
#define ISC_TIMEREVENT_ONCE (ISC_EVENTCLASS_TIMER + 1)
|
||||
|
||||
/***
|
||||
*** Timer and Timer Manager Functions
|
||||
***
|
||||
@@ -100,55 +81,40 @@ struct isc_timerevent {
|
||||
***/
|
||||
|
||||
void
|
||||
isc_timer_create(isc_timermgr_t *manager, isc_task_t *task,
|
||||
isc_taskaction_t action, void *arg, isc_timer_t **timerp);
|
||||
isc_timer_create(isc_loop_t *loop, isc_job_cb cb, void *cbarg,
|
||||
isc_timer_t **timerp);
|
||||
/*%<
|
||||
* 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'.
|
||||
* Create a new 'type' timer managed by 'loop'. The timers parameters are
|
||||
* specified by 'expires' and 'interval'. Events will be posted on the isc
|
||||
* event loop and when dispatched 'cb' will be called with 'cbarg' as the arg
|
||||
* value. The new timer is returned in 'timerp'.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'manager' is a valid manager
|
||||
*
|
||||
*\li 'task' is a valid task
|
||||
*
|
||||
*\li 'action' is a valid action
|
||||
*
|
||||
*\li 'expires' points to a valid time, or is NULL.
|
||||
*
|
||||
*\li 'interval' points to a valid interval, or is NULL.
|
||||
*
|
||||
*\li type == isc_timertype_inactive ||
|
||||
* ('expires' and 'interval' are not both 0)
|
||||
*
|
||||
*\li 'loop' is a valid manager
|
||||
*\li 'cb' is a valid job
|
||||
*\li 'timerp' is a valid pointer, and *timerp == NULL
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
*\li '*timerp' is attached to the newly created timer
|
||||
*
|
||||
*\li The timer is attached to the task
|
||||
*
|
||||
*\li An idle timeout will not be generated until at least Now + the
|
||||
* timer's interval if 'timer' is a once timer with a non-zero
|
||||
* interval.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*\li Success
|
||||
*\li No memory
|
||||
*\li Unexpected error
|
||||
*/
|
||||
|
||||
isc_result_t
|
||||
isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
|
||||
const isc_interval_t *interval, bool purge);
|
||||
void
|
||||
isc_timer_stop(isc_timer_t *timer);
|
||||
/*%<
|
||||
* Change the timer's type, and interval values to the given
|
||||
* values. If 'purge' is TRUE, any pending events from this timer
|
||||
* are purged from its task's event queue.
|
||||
* Stop the timer.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'timer' is a valid timer
|
||||
*/
|
||||
|
||||
void
|
||||
isc_timer_start(isc_timer_t *timer, isc_timertype_t type,
|
||||
const isc_interval_t *interval);
|
||||
/*%<
|
||||
* Start the timer.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
@@ -156,35 +122,28 @@ isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
|
||||
* 'interval' seconds.
|
||||
*
|
||||
*\li For once timers, 'interval' specifies how long the timer
|
||||
* can be idle before it generates an idle timeout. If 0, then no
|
||||
* idle timeout will be generated.
|
||||
* can be idle before it generates an idle timeout. If 0, then
|
||||
* the timer will be run immediately.
|
||||
*
|
||||
*\li If 'interval' is NULL, the zero interval will be used.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'timer' is a valid timer
|
||||
*
|
||||
*\li 'type' is either 'isc_timertype_ticker' or 'isc_timertype_once'
|
||||
*\li 'interval' points to a valid interval, or is NULL.
|
||||
*
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
*\li An idle timeout will not be generated until at least Now + the
|
||||
* timer's interval if 'timer' is a once timer with a non-zero
|
||||
* interval.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*\li Success
|
||||
*\li No memory
|
||||
*\li Unexpected error
|
||||
*/
|
||||
|
||||
void
|
||||
isc_timer_destroy(isc_timer_t **timerp);
|
||||
/*%<
|
||||
* Destroy *timerp.
|
||||
* Destroy the timer *timerp.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
@@ -193,33 +152,6 @@ isc_timer_destroy(isc_timer_t **timerp);
|
||||
* Ensures:
|
||||
*
|
||||
*\li *timerp is NULL.
|
||||
*
|
||||
*\code
|
||||
* The timer will be shutdown
|
||||
*
|
||||
* The timer will detach from its task
|
||||
*
|
||||
* All resources used by the timer have been freed
|
||||
*
|
||||
* Any events already posted by the timer will be purged.
|
||||
* Therefore, if isc_timer_destroy() is called in the context
|
||||
* of the timer's task, it is guaranteed that no more
|
||||
* timer event callbacks will run after the call.
|
||||
*
|
||||
* If this function is called from the timer event callback
|
||||
* the event itself must be destroyed before the timer
|
||||
* itself.
|
||||
*\endcode
|
||||
*/
|
||||
|
||||
isc_timertype_t
|
||||
isc_timer_gettype(isc_timer_t *timer);
|
||||
/*%<
|
||||
* Return the timer type.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'timer' to be a valid timer.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
@@ -11,21 +11,20 @@
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#include <isc/loop.h>
|
||||
#include <isc/managers.h>
|
||||
#include <isc/util.h>
|
||||
|
||||
#include "netmgr_p.h"
|
||||
#include "task_p.h"
|
||||
#include "timer_p.h"
|
||||
|
||||
isc_result_t
|
||||
isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
|
||||
isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
|
||||
isc_timermgr_t **timermgrp) {
|
||||
isc_loopmgr_t **loopmgrp, isc_nm_t **netmgrp,
|
||||
isc_taskmgr_t **taskmgrp) {
|
||||
isc_result_t result;
|
||||
isc_nm_t *netmgr = NULL;
|
||||
isc_taskmgr_t *taskmgr = NULL;
|
||||
isc_timermgr_t *timermgr = NULL;
|
||||
|
||||
REQUIRE(netmgrp != NULL && *netmgrp == NULL);
|
||||
isc__netmgr_create(mctx, workers, &netmgr);
|
||||
@@ -45,28 +44,18 @@ isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
|
||||
*taskmgrp = taskmgr;
|
||||
}
|
||||
|
||||
REQUIRE(timermgrp == NULL || *timermgrp == NULL);
|
||||
if (timermgrp != NULL) {
|
||||
result = isc__timermgr_create(mctx, &timermgr);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||
"isc_timermgr_create() failed: %s",
|
||||
isc_result_totext(result));
|
||||
goto fail;
|
||||
}
|
||||
*timermgrp = timermgr;
|
||||
}
|
||||
isc_loopmgr_create(mctx, workers, loopmgrp);
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
fail:
|
||||
isc_managers_destroy(netmgrp, taskmgrp, timermgrp);
|
||||
isc_managers_destroy(loopmgrp, netmgrp, taskmgrp);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
|
||||
isc_timermgr_t **timermgrp) {
|
||||
isc_managers_destroy(isc_loopmgr_t **loopmgrp, isc_nm_t **netmgrp,
|
||||
isc_taskmgr_t **taskmgrp) {
|
||||
/*
|
||||
* If we have a taskmgr to clean up, then we must also have a netmgr.
|
||||
*/
|
||||
@@ -109,11 +98,5 @@ isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
|
||||
isc__netmgr_destroy(netmgrp);
|
||||
}
|
||||
|
||||
/*
|
||||
* 5. Clean up the remaining managers.
|
||||
*/
|
||||
if (timermgrp != NULL) {
|
||||
INSIST(*timermgrp != NULL);
|
||||
isc__timermgr_destroy(timermgrp);
|
||||
}
|
||||
isc_loopmgr_destroy(loopmgrp);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/async.h>
|
||||
#include <isc/event.h>
|
||||
#include <isc/loop.h>
|
||||
#include <isc/mem.h>
|
||||
#include <isc/ratelimiter.h>
|
||||
#include <isc/refcount.h>
|
||||
@@ -25,74 +28,53 @@
|
||||
#include <isc/util.h>
|
||||
|
||||
typedef enum {
|
||||
isc_ratelimiter_stalled = 0,
|
||||
isc_ratelimiter_ratelimited = 1,
|
||||
isc_ratelimiter_idle = 2,
|
||||
isc_ratelimiter_shuttingdown = 3
|
||||
isc_ratelimiter_ratelimited = 0,
|
||||
isc_ratelimiter_idle = 1,
|
||||
isc_ratelimiter_shuttingdown = 2
|
||||
} isc_ratelimiter_state_t;
|
||||
|
||||
struct isc_ratelimiter {
|
||||
isc_mem_t *mctx;
|
||||
isc_mutex_t lock;
|
||||
isc_refcount_t references;
|
||||
isc_task_t *task;
|
||||
isc_loopmgr_t *loopmgr;
|
||||
isc_timer_t *timer;
|
||||
isc_interval_t interval;
|
||||
uint32_t pertic;
|
||||
bool pushpop;
|
||||
isc_ratelimiter_state_t state;
|
||||
isc_event_t shutdownevent;
|
||||
ISC_LIST(isc_event_t) pending;
|
||||
};
|
||||
|
||||
#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
|
||||
|
||||
static void
|
||||
ratelimiter_tick(isc_task_t *task, isc_event_t *event);
|
||||
|
||||
static void
|
||||
ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event);
|
||||
ratelimiter_tick(void *arg);
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
|
||||
isc_task_t *task, isc_ratelimiter_t **ratelimiterp) {
|
||||
isc_ratelimiter_t *rl;
|
||||
isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **ratelimiterp) {
|
||||
isc_ratelimiter_t *rl = NULL;
|
||||
isc_mem_t *mctx = isc_loop_getmctx(loop);
|
||||
|
||||
INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
|
||||
|
||||
rl = isc_mem_get(mctx, sizeof(*rl));
|
||||
*rl = (isc_ratelimiter_t){
|
||||
.mctx = mctx,
|
||||
.task = task,
|
||||
.pertic = 1,
|
||||
.state = isc_ratelimiter_idle,
|
||||
};
|
||||
|
||||
isc_refcount_init(&rl->references, 1);
|
||||
isc_mem_attach(mctx, &rl->mctx);
|
||||
isc_interval_set(&rl->interval, 0, 0);
|
||||
ISC_LIST_INIT(rl->pending);
|
||||
|
||||
isc_mutex_init(&rl->lock);
|
||||
|
||||
isc_timer_create(timermgr, rl->task, ratelimiter_tick, rl, &rl->timer);
|
||||
|
||||
/*
|
||||
* Increment the reference count to indicate that we may
|
||||
* (soon) have events outstanding.
|
||||
*/
|
||||
isc_refcount_increment(&rl->references);
|
||||
|
||||
ISC_EVENT_INIT(&rl->shutdownevent, sizeof(isc_event_t), 0,
|
||||
ISC_RATELIMITEREVENT_SHUTDOWN,
|
||||
ratelimiter_shutdowncomplete, rl, rl, NULL, NULL);
|
||||
isc_timer_create(loop, ratelimiter_tick, rl, &rl->timer);
|
||||
|
||||
*ratelimiterp = rl;
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
void
|
||||
isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(rl != NULL);
|
||||
REQUIRE(interval != NULL);
|
||||
|
||||
@@ -102,63 +84,63 @@ isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
|
||||
* If the timer is currently running, change its rate.
|
||||
*/
|
||||
if (rl->state == isc_ratelimiter_ratelimited) {
|
||||
result = isc_timer_reset(rl->timer, isc_timertype_ticker,
|
||||
&rl->interval, false);
|
||||
isc_timer_start(rl->timer, isc_timertype_ticker, &rl->interval);
|
||||
}
|
||||
UNLOCK(&rl->lock);
|
||||
return (result);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) {
|
||||
REQUIRE(rl != NULL);
|
||||
REQUIRE(pertic > 0);
|
||||
|
||||
if (pertic == 0) {
|
||||
pertic = 1;
|
||||
}
|
||||
LOCK(&rl->lock);
|
||||
rl->pertic = pertic;
|
||||
UNLOCK(&rl->lock);
|
||||
}
|
||||
|
||||
void
|
||||
isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop) {
|
||||
REQUIRE(rl != NULL);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
rl->pushpop = pushpop;
|
||||
UNLOCK(&rl->lock);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
|
||||
isc_event_t **eventp) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
isc_event_t *ev;
|
||||
isc_event_t *event;
|
||||
|
||||
REQUIRE(rl != NULL);
|
||||
REQUIRE(task != NULL);
|
||||
REQUIRE(eventp != NULL && *eventp != NULL);
|
||||
ev = *eventp;
|
||||
REQUIRE(ev->ev_sender == NULL);
|
||||
event = *eventp;
|
||||
REQUIRE(event->ev_sender == NULL);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
if (rl->state == isc_ratelimiter_ratelimited ||
|
||||
rl->state == isc_ratelimiter_stalled)
|
||||
{
|
||||
ev->ev_sender = task;
|
||||
switch (rl->state) {
|
||||
case isc_ratelimiter_shuttingdown:
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
break;
|
||||
case isc_ratelimiter_ratelimited:
|
||||
event->ev_sender = task;
|
||||
*eventp = NULL;
|
||||
if (rl->pushpop) {
|
||||
ISC_LIST_PREPEND(rl->pending, ev, ev_ratelink);
|
||||
ISC_LIST_PREPEND(rl->pending, event, ev_ratelink);
|
||||
} else {
|
||||
ISC_LIST_APPEND(rl->pending, ev, ev_ratelink);
|
||||
ISC_LIST_APPEND(rl->pending, event, ev_ratelink);
|
||||
}
|
||||
} else if (rl->state == isc_ratelimiter_idle) {
|
||||
result = isc_timer_reset(rl->timer, isc_timertype_ticker,
|
||||
&rl->interval, false);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
ev->ev_sender = task;
|
||||
rl->state = isc_ratelimiter_ratelimited;
|
||||
}
|
||||
} else {
|
||||
INSIST(rl->state == isc_ratelimiter_shuttingdown);
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
break;
|
||||
case isc_ratelimiter_idle:
|
||||
isc_timer_start(rl->timer, isc_timertype_ticker, &rl->interval);
|
||||
event->ev_sender = task;
|
||||
rl->state = isc_ratelimiter_ratelimited;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
UNLOCK(&rl->lock);
|
||||
if (*eventp != NULL && result == ISC_R_SUCCESS) {
|
||||
@@ -181,114 +163,73 @@ isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) {
|
||||
} else {
|
||||
result = ISC_R_NOTFOUND;
|
||||
}
|
||||
|
||||
if (ISC_LIST_EMPTY(rl->pending)) {
|
||||
/* No work left to do. Stop the timer. */
|
||||
isc_timer_stop(rl->timer);
|
||||
rl->state = isc_ratelimiter_idle;
|
||||
}
|
||||
UNLOCK(&rl->lock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
|
||||
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
|
||||
isc_event_t *p;
|
||||
ratelimiter_tick(void *arg) {
|
||||
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)arg;
|
||||
isc_event_t *event;
|
||||
uint32_t pertic;
|
||||
|
||||
UNUSED(task);
|
||||
|
||||
isc_event_free(&event);
|
||||
|
||||
pertic = rl->pertic;
|
||||
while (pertic != 0) {
|
||||
pertic--;
|
||||
LOCK(&rl->lock);
|
||||
p = ISC_LIST_HEAD(rl->pending);
|
||||
if (p != NULL) {
|
||||
event = ISC_LIST_HEAD(rl->pending);
|
||||
if (event != NULL) {
|
||||
/*
|
||||
* There is work to do. Let's do it after unlocking.
|
||||
*/
|
||||
ISC_LIST_UNLINK(rl->pending, p, ev_ratelink);
|
||||
ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
|
||||
} else {
|
||||
/*
|
||||
* No work left to do. Stop the timer so that we don't
|
||||
* waste resources by having it fire periodically.
|
||||
*/
|
||||
isc_result_t result = isc_timer_reset(
|
||||
rl->timer, isc_timertype_inactive, NULL, false);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
/* No work left to do. Stop the timer. */
|
||||
isc_timer_stop(rl->timer);
|
||||
rl->state = isc_ratelimiter_idle;
|
||||
pertic = 0; /* Force the loop to exit. */
|
||||
}
|
||||
UNLOCK(&rl->lock);
|
||||
if (p != NULL) {
|
||||
isc_task_t *evtask = p->ev_sender;
|
||||
isc_task_send(evtask, &p);
|
||||
if (event != NULL) {
|
||||
isc_task_send(event->ev_sender, &event);
|
||||
}
|
||||
INSIST(p == NULL);
|
||||
INSIST(event == NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
|
||||
isc_event_t *ev;
|
||||
isc_task_t *task;
|
||||
isc_result_t result;
|
||||
isc_event_t *event;
|
||||
|
||||
REQUIRE(rl != NULL);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
rl->state = isc_ratelimiter_shuttingdown;
|
||||
(void)isc_timer_reset(rl->timer, isc_timertype_inactive, NULL, false);
|
||||
while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
|
||||
task = ev->ev_sender;
|
||||
ISC_LIST_UNLINK(rl->pending, ev, ev_ratelink);
|
||||
ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
|
||||
isc_task_send(task, &ev);
|
||||
}
|
||||
task = NULL;
|
||||
isc_task_attach(rl->task, &task);
|
||||
|
||||
result = isc_timer_reset(rl->timer, isc_timertype_inactive, NULL,
|
||||
false);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
isc_timer_stop(rl->timer);
|
||||
isc_timer_destroy(&rl->timer);
|
||||
|
||||
/*
|
||||
* Send an event to our task. The delivery of this event
|
||||
* indicates that no more timer events will be delivered.
|
||||
*/
|
||||
ev = &rl->shutdownevent;
|
||||
isc_task_send(rl->task, &ev);
|
||||
while ((event = ISC_LIST_HEAD(rl->pending)) != NULL) {
|
||||
ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
|
||||
UNLOCK(&rl->lock);
|
||||
|
||||
event->ev_attributes |= ISC_EVENTATTR_CANCELED;
|
||||
isc_task_send(event->ev_sender, &event);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
}
|
||||
|
||||
UNLOCK(&rl->lock);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) {
|
||||
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
|
||||
|
||||
UNUSED(task);
|
||||
|
||||
isc_ratelimiter_detach(&rl);
|
||||
isc_task_detach(&task);
|
||||
}
|
||||
|
||||
static void
|
||||
ratelimiter_free(isc_ratelimiter_t *rl) {
|
||||
isc_refcount_destroy(&rl->references);
|
||||
isc_mutex_destroy(&rl->lock);
|
||||
isc_mem_put(rl->mctx, rl, sizeof(*rl));
|
||||
}
|
||||
|
||||
void
|
||||
isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) {
|
||||
REQUIRE(source != NULL);
|
||||
REQUIRE(target != NULL && *target == NULL);
|
||||
|
||||
isc_refcount_increment(&source->references);
|
||||
|
||||
*target = source;
|
||||
}
|
||||
|
||||
void
|
||||
isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
|
||||
isc_ratelimiter_destroy(isc_ratelimiter_t **rlp) {
|
||||
isc_ratelimiter_t *rl;
|
||||
|
||||
REQUIRE(rlp != NULL && *rlp != NULL);
|
||||
@@ -296,63 +237,10 @@ isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
|
||||
rl = *rlp;
|
||||
*rlp = NULL;
|
||||
|
||||
if (isc_refcount_decrement(&rl->references) == 1) {
|
||||
ratelimiter_free(rl);
|
||||
}
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_stall(isc_ratelimiter_t *rl) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(rl != NULL);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
switch (rl->state) {
|
||||
case isc_ratelimiter_shuttingdown:
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
break;
|
||||
case isc_ratelimiter_ratelimited:
|
||||
result = isc_timer_reset(rl->timer, isc_timertype_inactive,
|
||||
NULL, false);
|
||||
RUNTIME_CHECK(result == ISC_R_SUCCESS);
|
||||
FALLTHROUGH;
|
||||
case isc_ratelimiter_idle:
|
||||
case isc_ratelimiter_stalled:
|
||||
rl->state = isc_ratelimiter_stalled;
|
||||
break;
|
||||
}
|
||||
REQUIRE(rl->state == isc_ratelimiter_shuttingdown);
|
||||
UNLOCK(&rl->lock);
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_ratelimiter_release(isc_ratelimiter_t *rl) {
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
REQUIRE(rl != NULL);
|
||||
|
||||
LOCK(&rl->lock);
|
||||
switch (rl->state) {
|
||||
case isc_ratelimiter_shuttingdown:
|
||||
result = ISC_R_SHUTTINGDOWN;
|
||||
break;
|
||||
case isc_ratelimiter_stalled:
|
||||
if (!ISC_LIST_EMPTY(rl->pending)) {
|
||||
result = isc_timer_reset(rl->timer,
|
||||
isc_timertype_ticker,
|
||||
&rl->interval, false);
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
rl->state = isc_ratelimiter_ratelimited;
|
||||
}
|
||||
} else {
|
||||
rl->state = isc_ratelimiter_idle;
|
||||
}
|
||||
break;
|
||||
case isc_ratelimiter_ratelimited:
|
||||
case isc_ratelimiter_idle:
|
||||
break;
|
||||
}
|
||||
UNLOCK(&rl->lock);
|
||||
return (result);
|
||||
|
||||
isc_mutex_destroy(&rl->lock);
|
||||
isc_mem_putanddetach(&rl->mctx, rl, sizeof(*rl));
|
||||
}
|
||||
|
||||
667
lib/isc/timer.c
667
lib/isc/timer.c
@@ -15,9 +15,10 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <isc/app.h>
|
||||
#include <isc/async.h>
|
||||
#include <isc/condition.h>
|
||||
#include <isc/heap.h>
|
||||
#include <isc/job.h>
|
||||
#include <isc/log.h>
|
||||
#include <isc/magic.h>
|
||||
#include <isc/mem.h>
|
||||
@@ -29,552 +30,198 @@
|
||||
#include <isc/time.h>
|
||||
#include <isc/timer.h>
|
||||
#include <isc/util.h>
|
||||
#include <isc/uv.h>
|
||||
|
||||
#include "timer_p.h"
|
||||
|
||||
#ifdef ISC_TIMER_TRACE
|
||||
#define XTRACE(s) fprintf(stderr, "%s\n", (s))
|
||||
#define XTRACEID(s, t) fprintf(stderr, "%s %p\n", (s), (t))
|
||||
#define XTRACETIME(s, d) \
|
||||
fprintf(stderr, "%s %u.%09u\n", (s), (d).seconds, (d).nanoseconds)
|
||||
#define XTRACETIME2(s, d, n) \
|
||||
fprintf(stderr, "%s %u.%09u %u.%09u\n", (s), (d).seconds, \
|
||||
(d).nanoseconds, (n).seconds, (n).nanoseconds)
|
||||
#define XTRACETIMER(s, t, d) \
|
||||
fprintf(stderr, "%s %p %u.%09u\n", (s), (t), (d).seconds, \
|
||||
(d).nanoseconds)
|
||||
#else /* ifdef ISC_TIMER_TRACE */
|
||||
#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 */
|
||||
#include "loop_p.h"
|
||||
|
||||
#define TIMER_MAGIC ISC_MAGIC('T', 'I', 'M', 'R')
|
||||
#define VALID_TIMER(t) ISC_MAGIC_VALID(t, TIMER_MAGIC)
|
||||
|
||||
struct isc_timer {
|
||||
/*! Not locked. */
|
||||
unsigned int magic;
|
||||
isc_timermgr_t *manager;
|
||||
isc_mutex_t lock;
|
||||
/*! Locked by timer lock. */
|
||||
isc_time_t idle;
|
||||
ISC_LIST(isc_timerevent_t) active;
|
||||
/*! Locked by manager lock. */
|
||||
isc_timertype_t type;
|
||||
isc_interval_t interval;
|
||||
isc_task_t *task;
|
||||
isc_taskaction_t action;
|
||||
void *arg;
|
||||
unsigned int index;
|
||||
isc_time_t due;
|
||||
LINK(isc_timer_t) link;
|
||||
};
|
||||
|
||||
#define TIMER_MANAGER_MAGIC ISC_MAGIC('T', 'I', 'M', 'M')
|
||||
#define VALID_MANAGER(m) ISC_MAGIC_VALID(m, TIMER_MANAGER_MAGIC)
|
||||
|
||||
struct isc_timermgr {
|
||||
/* Not locked. */
|
||||
unsigned int magic;
|
||||
isc_mem_t *mctx;
|
||||
isc_mutex_t lock;
|
||||
/* Locked by manager lock. */
|
||||
bool done;
|
||||
LIST(isc_timer_t) timers;
|
||||
unsigned int nscheduled;
|
||||
isc_time_t due;
|
||||
isc_condition_t wakeup;
|
||||
isc_thread_t thread;
|
||||
isc_heap_t *heap;
|
||||
};
|
||||
|
||||
static isc_result_t
|
||||
schedule(isc_timer_t *timer, isc_time_t *now, bool signal_ok) {
|
||||
isc_timermgr_t *manager;
|
||||
isc_time_t due;
|
||||
isc_result_t result = ISC_R_SUCCESS;
|
||||
|
||||
/*!
|
||||
* Note: the caller must ensure locking.
|
||||
*/
|
||||
|
||||
manager = timer->manager;
|
||||
isc_refcount_t references;
|
||||
isc_loop_t *loop;
|
||||
uv_timer_t timer;
|
||||
isc_job_cb cb;
|
||||
void *cbarg;
|
||||
|
||||
/*
|
||||
* Compute the new due time.
|
||||
* We are locking the values here for now, but this needs to go away
|
||||
* when the timers are pinned to the respective loops.
|
||||
*/
|
||||
switch (timer->type) {
|
||||
case isc_timertype_ticker:
|
||||
result = isc_time_add(now, &timer->interval, &due);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
break;
|
||||
isc_mutex_t lock;
|
||||
uint64_t timeout;
|
||||
uint64_t repeat;
|
||||
};
|
||||
|
||||
static void
|
||||
isc__timer_detach(isc_timer_t **timerp);
|
||||
|
||||
void
|
||||
isc_timer_create(isc_loop_t *loop, isc_job_cb cb, void *cbarg,
|
||||
isc_timer_t **timerp) {
|
||||
int r;
|
||||
isc_timer_t *timer = NULL;
|
||||
isc_loopmgr_t *loopmgr = NULL;
|
||||
|
||||
REQUIRE(cb != NULL);
|
||||
REQUIRE(timerp != NULL && *timerp == NULL);
|
||||
|
||||
REQUIRE(VALID_LOOP(loop));
|
||||
|
||||
loopmgr = loop->loopmgr;
|
||||
|
||||
REQUIRE(VALID_LOOPMGR(loopmgr));
|
||||
|
||||
REQUIRE(loop == isc_loop_current(loopmgr) ||
|
||||
!atomic_load(&loopmgr->running) ||
|
||||
atomic_load(&loopmgr->paused) > 0);
|
||||
|
||||
timer = isc_mem_get(loop->mctx, sizeof(*timer));
|
||||
*timer = (isc_timer_t){
|
||||
.cb = cb,
|
||||
.cbarg = cbarg,
|
||||
};
|
||||
|
||||
isc_loop_attach(loop, &timer->loop);
|
||||
|
||||
isc_refcount_init(&timer->references, 1);
|
||||
|
||||
isc_mutex_init(&timer->lock);
|
||||
|
||||
timer->magic = TIMER_MAGIC;
|
||||
|
||||
r = uv_timer_init(&timer->loop->loop, &timer->timer);
|
||||
UV_RUNTIME_CHECK(uv_timer_init, r);
|
||||
uv_handle_set_data(&timer->timer, timer);
|
||||
|
||||
*timerp = timer;
|
||||
}
|
||||
|
||||
static void
|
||||
isc__timer_stop(void *arg) {
|
||||
isc_timer_t *timer = (isc_timer_t *)arg;
|
||||
uv_timer_stop(&timer->timer);
|
||||
isc__timer_detach(&timer);
|
||||
}
|
||||
|
||||
void
|
||||
isc_timer_stop(isc_timer_t *timer) {
|
||||
REQUIRE(VALID_TIMER(timer));
|
||||
|
||||
isc_refcount_increment(&timer->references);
|
||||
if (timer->loop == isc_loop_current(timer->loop->loopmgr)) {
|
||||
isc__timer_stop(timer);
|
||||
} else {
|
||||
isc_async_run(timer->loop, isc__timer_stop, timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timer_cb(uv_timer_t *handle) {
|
||||
isc_timer_t *timer = uv_handle_get_data(handle);
|
||||
|
||||
REQUIRE(VALID_TIMER(timer));
|
||||
|
||||
timer->cb(timer->cbarg);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__timer_start(void *arg) {
|
||||
isc_timer_t *timer = (isc_timer_t *)arg;
|
||||
|
||||
LOCK(&timer->lock);
|
||||
int r = uv_timer_start(&timer->timer, timer_cb, timer->timeout,
|
||||
timer->repeat);
|
||||
UV_RUNTIME_CHECK(uv_timer_start, r);
|
||||
UNLOCK(&timer->lock);
|
||||
|
||||
isc__timer_detach(&timer);
|
||||
}
|
||||
|
||||
void
|
||||
isc_timer_start(isc_timer_t *timer, isc_timertype_t type,
|
||||
const isc_interval_t *interval) {
|
||||
isc_loopmgr_t *loopmgr = NULL;
|
||||
isc_loop_t *loop = NULL;
|
||||
|
||||
REQUIRE(VALID_TIMER(timer));
|
||||
REQUIRE(type == isc_timertype_ticker || type == isc_timertype_once);
|
||||
|
||||
loop = timer->loop;
|
||||
|
||||
REQUIRE(VALID_LOOP(loop));
|
||||
|
||||
loopmgr = loop->loopmgr;
|
||||
|
||||
REQUIRE(VALID_LOOPMGR(loopmgr));
|
||||
|
||||
LOCK(&timer->lock);
|
||||
switch (type) {
|
||||
case isc_timertype_once:
|
||||
due = timer->idle;
|
||||
timer->timeout = isc_interval_ms(interval);
|
||||
timer->repeat = 0;
|
||||
break;
|
||||
case isc_timertype_ticker:
|
||||
timer->timeout = timer->repeat = isc_interval_ms(interval);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/*
|
||||
* Schedule the timer.
|
||||
*/
|
||||
|
||||
if (timer->index > 0) {
|
||||
/*
|
||||
* Already scheduled.
|
||||
*/
|
||||
int cmp = isc_time_compare(&due, &timer->due);
|
||||
timer->due = due;
|
||||
switch (cmp) {
|
||||
case -1:
|
||||
isc_heap_increased(manager->heap, timer->index);
|
||||
break;
|
||||
case 1:
|
||||
isc_heap_decreased(manager->heap, timer->index);
|
||||
break;
|
||||
case 0:
|
||||
/* Nothing to do. */
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
timer->due = due;
|
||||
isc_heap_insert(manager->heap, timer);
|
||||
manager->nscheduled++;
|
||||
}
|
||||
|
||||
XTRACETIMER("schedule", timer, due);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
if (timer->index == 1 && signal_ok) {
|
||||
XTRACE("signal (schedule)");
|
||||
SIGNAL(&manager->wakeup);
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
static void
|
||||
deschedule(isc_timer_t *timer) {
|
||||
isc_timermgr_t *manager;
|
||||
|
||||
/*
|
||||
* The caller must ensure locking.
|
||||
*/
|
||||
|
||||
manager = timer->manager;
|
||||
if (timer->index > 0) {
|
||||
bool need_wakeup = false;
|
||||
if (timer->index == 1) {
|
||||
need_wakeup = true;
|
||||
}
|
||||
isc_heap_delete(manager->heap, timer->index);
|
||||
timer->index = 0;
|
||||
INSIST(manager->nscheduled > 0);
|
||||
manager->nscheduled--;
|
||||
if (need_wakeup) {
|
||||
XTRACE("signal (deschedule)");
|
||||
SIGNAL(&manager->wakeup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timerevent_unlink(isc_timer_t *timer, isc_timerevent_t *event) {
|
||||
REQUIRE(ISC_LINK_LINKED(event, ev_timerlink));
|
||||
ISC_LIST_UNLINK(timer->active, event, ev_timerlink);
|
||||
}
|
||||
|
||||
static void
|
||||
timerevent_destroy(isc_event_t *event0) {
|
||||
isc_timer_t *timer = event0->ev_destroy_arg;
|
||||
isc_timerevent_t *event = (isc_timerevent_t *)event0;
|
||||
|
||||
LOCK(&timer->lock);
|
||||
if (ISC_LINK_LINKED(event, ev_timerlink)) {
|
||||
/* The event was unlinked via timer_purge() */
|
||||
timerevent_unlink(timer, event);
|
||||
}
|
||||
UNLOCK(&timer->lock);
|
||||
|
||||
isc_mem_put(timer->manager->mctx, event, event0->ev_size);
|
||||
isc_refcount_increment(&timer->references);
|
||||
if (timer->loop == isc_loop_current(timer->loop->loopmgr)) {
|
||||
isc__timer_start(timer);
|
||||
} else {
|
||||
isc_async_run(timer->loop, isc__timer_start, timer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
timer_purge(isc_timer_t *timer) {
|
||||
isc_timerevent_t *event = NULL;
|
||||
|
||||
while ((event = ISC_LIST_HEAD(timer->active)) != NULL) {
|
||||
timerevent_unlink(timer, event);
|
||||
UNLOCK(&timer->lock);
|
||||
(void)isc_task_purgeevent(timer->task, (isc_event_t *)event);
|
||||
LOCK(&timer->lock);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
isc_timer_create(isc_timermgr_t *manager, isc_task_t *task,
|
||||
isc_taskaction_t action, void *arg, isc_timer_t **timerp) {
|
||||
REQUIRE(VALID_MANAGER(manager));
|
||||
REQUIRE(task != NULL);
|
||||
REQUIRE(action != NULL);
|
||||
|
||||
isc_timer_t *timer;
|
||||
isc_time_t now;
|
||||
|
||||
REQUIRE(timerp != NULL && *timerp == NULL);
|
||||
|
||||
/*
|
||||
* Get current time.
|
||||
*/
|
||||
TIME_NOW(&now);
|
||||
|
||||
timer = isc_mem_get(manager->mctx, sizeof(*timer));
|
||||
*timer = (isc_timer_t){
|
||||
.manager = manager,
|
||||
.type = isc_timertype_inactive,
|
||||
.interval = *isc_interval_zero,
|
||||
.action = action,
|
||||
.arg = arg,
|
||||
};
|
||||
|
||||
isc_time_settoepoch(&timer->idle);
|
||||
|
||||
isc_task_attach(task, &timer->task);
|
||||
|
||||
isc_mutex_init(&timer->lock);
|
||||
ISC_LINK_INIT(timer, link);
|
||||
|
||||
ISC_LIST_INIT(timer->active);
|
||||
|
||||
timer->magic = TIMER_MAGIC;
|
||||
|
||||
/*
|
||||
* Note we don't have to lock the timer like we normally would because
|
||||
* there are no external references to it yet.
|
||||
*/
|
||||
|
||||
*timerp = timer;
|
||||
|
||||
LOCK(&manager->lock);
|
||||
APPEND(manager->timers, timer, link);
|
||||
UNLOCK(&manager->lock);
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc_timer_reset(isc_timer_t *timer, isc_timertype_t type,
|
||||
const isc_interval_t *interval, bool purge) {
|
||||
isc_time_t now;
|
||||
isc_timermgr_t *manager;
|
||||
isc_result_t result;
|
||||
|
||||
/*
|
||||
* Change the timer's type, expires, and interval values to the given
|
||||
* values. If 'purge' is true, any pending events from this timer
|
||||
* are purged from its task's event queue.
|
||||
*/
|
||||
|
||||
REQUIRE(VALID_TIMER(timer));
|
||||
manager = timer->manager;
|
||||
REQUIRE(VALID_MANAGER(manager));
|
||||
|
||||
if (interval == NULL) {
|
||||
interval = isc_interval_zero;
|
||||
}
|
||||
REQUIRE(type == isc_timertype_inactive ||
|
||||
!isc_interval_iszero(interval));
|
||||
|
||||
/*
|
||||
* Get current time.
|
||||
*/
|
||||
if (type != isc_timertype_inactive) {
|
||||
TIME_NOW(&now);
|
||||
} 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.
|
||||
*/
|
||||
isc_time_settoepoch(&now);
|
||||
}
|
||||
|
||||
LOCK(&manager->lock);
|
||||
LOCK(&timer->lock);
|
||||
|
||||
if (purge) {
|
||||
timer_purge(timer);
|
||||
}
|
||||
timer->type = type;
|
||||
timer->interval = *interval;
|
||||
if (type == isc_timertype_once && !isc_interval_iszero(interval)) {
|
||||
result = isc_time_add(&now, interval, &timer->idle);
|
||||
} else {
|
||||
isc_time_settoepoch(&timer->idle);
|
||||
result = ISC_R_SUCCESS;
|
||||
}
|
||||
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
if (type == isc_timertype_inactive) {
|
||||
deschedule(timer);
|
||||
result = ISC_R_SUCCESS;
|
||||
} else {
|
||||
result = schedule(timer, &now, true);
|
||||
}
|
||||
}
|
||||
|
||||
UNLOCK(&timer->lock);
|
||||
UNLOCK(&manager->lock);
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
||||
isc_timertype_t
|
||||
isc_timer_gettype(isc_timer_t *timer) {
|
||||
isc_timertype_t t;
|
||||
timer_destroy(uv_handle_t *handle) {
|
||||
isc_timer_t *timer = uv_handle_get_data(handle);
|
||||
isc_loop_t *loop;
|
||||
|
||||
REQUIRE(VALID_TIMER(timer));
|
||||
|
||||
LOCK(&timer->lock);
|
||||
t = timer->type;
|
||||
UNLOCK(&timer->lock);
|
||||
loop = timer->loop;
|
||||
|
||||
return (t);
|
||||
isc_refcount_destroy(&timer->references);
|
||||
isc_mutex_destroy(&timer->lock);
|
||||
|
||||
isc_mem_put(loop->mctx, timer, sizeof(*timer));
|
||||
|
||||
isc_loop_detach(&loop);
|
||||
}
|
||||
|
||||
void
|
||||
isc_timer_destroy(isc_timer_t **timerp) {
|
||||
static void
|
||||
isc__timer_destroy(void *arg) {
|
||||
isc_timer_t *timer = (isc_timer_t *)arg;
|
||||
|
||||
uv_timer_stop(&timer->timer);
|
||||
uv_close(&timer->timer, timer_destroy);
|
||||
}
|
||||
|
||||
static void
|
||||
isc__timer_detach(isc_timer_t **timerp) {
|
||||
isc_timer_t *timer = NULL;
|
||||
isc_timermgr_t *manager = NULL;
|
||||
|
||||
REQUIRE(timerp != NULL && VALID_TIMER(*timerp));
|
||||
|
||||
timer = *timerp;
|
||||
*timerp = NULL;
|
||||
|
||||
manager = timer->manager;
|
||||
|
||||
LOCK(&manager->lock);
|
||||
|
||||
LOCK(&timer->lock);
|
||||
timer_purge(timer);
|
||||
deschedule(timer);
|
||||
UNLOCK(&timer->lock);
|
||||
|
||||
UNLINK(manager->timers, timer, link);
|
||||
|
||||
UNLOCK(&manager->lock);
|
||||
|
||||
isc_task_detach(&timer->task);
|
||||
isc_mutex_destroy(&timer->lock);
|
||||
timer->magic = 0;
|
||||
isc_mem_put(manager->mctx, timer, sizeof(*timer));
|
||||
}
|
||||
|
||||
static void
|
||||
post_event(isc_timermgr_t *manager, isc_timer_t *timer, isc_eventtype_t type) {
|
||||
isc_timerevent_t *event;
|
||||
XTRACEID("posting", timer);
|
||||
|
||||
event = (isc_timerevent_t *)isc_event_allocate(
|
||||
manager->mctx, timer, type, timer->action, timer->arg,
|
||||
sizeof(*event));
|
||||
|
||||
ISC_LINK_INIT(event, ev_timerlink);
|
||||
((isc_event_t *)event)->ev_destroy = timerevent_destroy;
|
||||
((isc_event_t *)event)->ev_destroy_arg = timer;
|
||||
|
||||
event->due = timer->due;
|
||||
|
||||
LOCK(&timer->lock);
|
||||
ISC_LIST_APPEND(timer->active, event, ev_timerlink);
|
||||
UNLOCK(&timer->lock);
|
||||
|
||||
isc_task_send(timer->task, ISC_EVENT_PTR(&event));
|
||||
}
|
||||
|
||||
static void
|
||||
dispatch(isc_timermgr_t *manager, isc_time_t *now) {
|
||||
bool need_schedule;
|
||||
isc_eventtype_t type = 0;
|
||||
isc_timer_t *timer;
|
||||
isc_result_t result;
|
||||
|
||||
/*!
|
||||
* The caller must be holding the manager lock.
|
||||
*/
|
||||
|
||||
while (manager->nscheduled > 0) {
|
||||
timer = isc_heap_element(manager->heap, 1);
|
||||
INSIST(timer != NULL && timer->type != isc_timertype_inactive);
|
||||
|
||||
if (isc_time_compare(now, &timer->due) < 0) {
|
||||
manager->due = timer->due;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (timer->type) {
|
||||
case isc_timertype_ticker:
|
||||
type = ISC_TIMEREVENT_TICK;
|
||||
post_event(manager, timer, type);
|
||||
need_schedule = true;
|
||||
break;
|
||||
case isc_timertype_once:
|
||||
type = ISC_TIMEREVENT_ONCE;
|
||||
post_event(manager, timer, type);
|
||||
need_schedule = false;
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
timer->index = 0;
|
||||
isc_heap_delete(manager->heap, 1);
|
||||
manager->nscheduled--;
|
||||
|
||||
if (need_schedule) {
|
||||
result = schedule(timer, now, false);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__, "%s: %u",
|
||||
"couldn't schedule "
|
||||
"timer",
|
||||
result);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static isc_threadresult_t
|
||||
run(void *uap) {
|
||||
isc_timermgr_t *manager = uap;
|
||||
isc_time_t now;
|
||||
isc_result_t result;
|
||||
|
||||
LOCK(&manager->lock);
|
||||
while (!manager->done) {
|
||||
TIME_NOW(&now);
|
||||
|
||||
XTRACETIME("running", now);
|
||||
|
||||
dispatch(manager, &now);
|
||||
|
||||
if (manager->nscheduled > 0) {
|
||||
XTRACETIME2("waituntil", manager->due, now);
|
||||
result = WAITUNTIL(&manager->wakeup, &manager->lock,
|
||||
&manager->due);
|
||||
INSIST(result == ISC_R_SUCCESS ||
|
||||
result == ISC_R_TIMEDOUT);
|
||||
if (isc_refcount_decrement(&timer->references) == 1) {
|
||||
if (timer->loop == isc_loop_current(timer->loop->loopmgr)) {
|
||||
isc__timer_destroy(timer);
|
||||
} else {
|
||||
XTRACETIME("wait", now);
|
||||
WAIT(&manager->wakeup, &manager->lock);
|
||||
isc_async_run(timer->loop, isc__timer_destroy, timer);
|
||||
}
|
||||
XTRACE("wakeup");
|
||||
}
|
||||
UNLOCK(&manager->lock);
|
||||
|
||||
return ((isc_threadresult_t)0);
|
||||
}
|
||||
|
||||
static bool
|
||||
sooner(void *v1, void *v2) {
|
||||
isc_timer_t *t1, *t2;
|
||||
|
||||
t1 = v1;
|
||||
t2 = v2;
|
||||
REQUIRE(VALID_TIMER(t1));
|
||||
REQUIRE(VALID_TIMER(t2));
|
||||
|
||||
if (isc_time_compare(&t1->due, &t2->due) < 0) {
|
||||
return (true);
|
||||
}
|
||||
return (false);
|
||||
}
|
||||
|
||||
static void
|
||||
set_index(void *what, unsigned int index) {
|
||||
isc_timer_t *timer;
|
||||
|
||||
REQUIRE(VALID_TIMER(what));
|
||||
timer = what;
|
||||
|
||||
timer->index = index;
|
||||
}
|
||||
|
||||
isc_result_t
|
||||
isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp) {
|
||||
isc_timermgr_t *manager;
|
||||
|
||||
/*
|
||||
* Create a timer manager.
|
||||
*/
|
||||
|
||||
REQUIRE(managerp != NULL && *managerp == NULL);
|
||||
|
||||
manager = isc_mem_get(mctx, sizeof(*manager));
|
||||
|
||||
manager->magic = TIMER_MANAGER_MAGIC;
|
||||
manager->mctx = NULL;
|
||||
manager->done = false;
|
||||
INIT_LIST(manager->timers);
|
||||
manager->nscheduled = 0;
|
||||
isc_time_settoepoch(&manager->due);
|
||||
manager->heap = NULL;
|
||||
isc_heap_create(mctx, sooner, set_index, 0, &manager->heap);
|
||||
isc_mutex_init(&manager->lock);
|
||||
isc_mem_attach(mctx, &manager->mctx);
|
||||
isc_condition_init(&manager->wakeup);
|
||||
isc_thread_create(run, manager, &manager->thread);
|
||||
isc_thread_setname(manager->thread, "isc-timer");
|
||||
|
||||
*managerp = manager;
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
void
|
||||
isc__timermgr_destroy(isc_timermgr_t **managerp) {
|
||||
isc_timermgr_t *manager;
|
||||
|
||||
/*
|
||||
* Destroy a timer manager.
|
||||
*/
|
||||
|
||||
REQUIRE(managerp != NULL);
|
||||
manager = *managerp;
|
||||
REQUIRE(VALID_MANAGER(manager));
|
||||
|
||||
LOCK(&manager->lock);
|
||||
|
||||
REQUIRE(EMPTY(manager->timers));
|
||||
manager->done = true;
|
||||
|
||||
XTRACE("signal (destroy)");
|
||||
SIGNAL(&manager->wakeup);
|
||||
|
||||
UNLOCK(&manager->lock);
|
||||
|
||||
/*
|
||||
* Wait for thread to exit.
|
||||
*/
|
||||
isc_thread_join(manager->thread, NULL);
|
||||
|
||||
/*
|
||||
* Clean up.
|
||||
*/
|
||||
isc_condition_destroy(&manager->wakeup);
|
||||
isc_mutex_destroy(&manager->lock);
|
||||
isc_heap_destroy(&manager->heap);
|
||||
manager->magic = 0;
|
||||
isc_mem_putanddetach(&manager->mctx, manager, sizeof(*manager));
|
||||
|
||||
*managerp = NULL;
|
||||
isc_timer_destroy(isc_timer_t **timerp) {
|
||||
isc__timer_detach(timerp);
|
||||
}
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* SPDX-License-Identifier: MPL-2.0
|
||||
*
|
||||
* 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 https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <isc/mem.h>
|
||||
#include <isc/result.h>
|
||||
#include <isc/timer.h>
|
||||
|
||||
isc_result_t
|
||||
isc__timermgr_create(isc_mem_t *mctx, isc_timermgr_t **managerp);
|
||||
/*%<
|
||||
* Create a timer manager.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*\li All memory will be allocated in memory context 'mctx'.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li 'mctx' is a valid memory context.
|
||||
*
|
||||
*\li 'managerp' points to a NULL isc_timermgr_t.
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
*\li '*managerp' is a valid isc_timermgr_t.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
*\li Success
|
||||
*\li No memory
|
||||
*\li Unexpected error
|
||||
*/
|
||||
|
||||
void
|
||||
isc__timermgr_destroy(isc_timermgr_t **managerp);
|
||||
/*%<
|
||||
* Destroy a timer manager.
|
||||
*
|
||||
* Notes:
|
||||
*
|
||||
*\li This routine blocks until there are no timers left in the manager,
|
||||
* so if the caller holds any timer references using the manager, it
|
||||
* must detach them before calling isc_timermgr_destroy() or it will
|
||||
* block forever.
|
||||
*
|
||||
* Requires:
|
||||
*
|
||||
*\li '*managerp' is a valid isc_timermgr_t.
|
||||
*
|
||||
* Ensures:
|
||||
*
|
||||
*\li *managerp == NULL
|
||||
*
|
||||
*\li All resources used by the manager have been freed.
|
||||
*/
|
||||
Reference in New Issue
Block a user