From 16a107c904a30a687a08efec86a26a2f9398d2ed Mon Sep 17 00:00:00 2001 From: Andreas Gustafsson Date: Wed, 26 Apr 2000 17:10:32 +0000 Subject: [PATCH] rate limiter now has separate shutdown() and destroy() functions, and it guarantees that all queued events are delivered even in the shutdown case --- CHANGES | 4 ++ lib/isc/include/isc/ratelimiter.h | 35 +++++++++++++---- lib/isc/ratelimiter.c | 62 +++++++++++++++++++++++++------ 3 files changed, 82 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index 61495b908f..ff70e07e34 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ + 99. [cleanup] Rate limiter now has separate shutdown() and + destroy() functions, and it guarantees that all + queued events are delivered even in the shutdown case. + 98. [cleanup] does not need or unless ISC_PLATFORM_NEEDVSNPRINTF is defined. diff --git a/lib/isc/include/isc/ratelimiter.h b/lib/isc/include/isc/ratelimiter.h index 9900a0fde9..2a9258b502 100644 --- a/lib/isc/include/isc/ratelimiter.h +++ b/lib/isc/include/isc/ratelimiter.h @@ -43,11 +43,6 @@ ISC_LANG_BEGINDECLS typedef struct isc_ratelimiter isc_ratelimiter_t; -typedef enum { - isc_ratelimiter_ratelimited, - isc_ratelimiter_worklimited -} isc_ratelimiter_state_t; - /***** ***** Functions. *****/ @@ -57,7 +52,7 @@ isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_task_t *task, isc_ratelimiter_t **ratelimiterp); /* * Create a rate limiter. It will execute events in the context - * of 'task' with a guaranteed minimum interval, initially zero. + * of 'task'. The execution interval is initially undefined. */ isc_result_t @@ -65,6 +60,9 @@ isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval); /* * Set the mininum interval between event executions. * The interval value is copied, so the caller need not preserve it. + * + * Requires: + * '*interval' is a nonzero interval. */ isc_result_t @@ -74,14 +72,35 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_event_t **eventp); * to doing an isc_task_send() to the rate limiter's task, except * that the execution may be delayed to achieve the desired rate * of execution. + * + * Requires: + * An interval has been set by calling + * isc_ratelimiter_setinterval(). + */ + +void +isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter); +/* + * Shut down a rate limiter. All events that have not yet been + * dispatched to the task are dispatched immediately with + * the ISC_EVENTATTR_CANCELED bit set in ev_attributes. + * Further attempts to enqueue events will fail with + * ISC_R_SHUTTINGDOWN. */ void isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp); /* - * Destroy a rate limiter. All events that have not yet been - * dispatched to the task are freed immedately. + * Destroy a rate limiter. * Does not destroy the task or events already queued on it. + * + * Requires: + * The rate limiter to have been shut down. + * + * Ensures: + * Resources used by the rate limiter will be freed. */ +ISC_LANG_ENDDECLS + #endif /* ISC_RATELIMITER_H */ diff --git a/lib/isc/ratelimiter.c b/lib/isc/ratelimiter.c index 02b22f3759..0130f0aeae 100644 --- a/lib/isc/ratelimiter.c +++ b/lib/isc/ratelimiter.c @@ -24,6 +24,12 @@ #include #include +typedef enum { + isc_ratelimiter_ratelimited, + isc_ratelimiter_worklimited, + isc_ratelimiter_shuttingdown +} isc_ratelimiter_state_t; + struct isc_ratelimiter { isc_mem_t * mctx; isc_mutex_t lock; @@ -31,11 +37,23 @@ struct isc_ratelimiter { isc_timer_t * timer; isc_interval_t interval; 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) +{ + isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; + UNUSED(task); + isc_mutex_destroy(&rl->lock); + isc_mem_put(rl->mctx, rl, sizeof(*rl)); +} + isc_result_t isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_task_t *task, isc_ratelimiter_t **ratelimiterp) @@ -63,6 +81,11 @@ isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, if (result != ISC_R_SUCCESS) goto free_mutex; + ISC_EVENT_INIT(&rl->shutdownevent, + sizeof(isc_event_t), + 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN, + ratelimiter_shutdowncomplete, rl, rl, NULL, NULL); + *ratelimiterp = rl; return (ISC_R_SUCCESS); @@ -98,11 +121,14 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_event_t **eventp) { isc_event_t *ev = *eventp; ISC_LIST_APPEND(rl->pending, ev, ev_link); *eventp = NULL; - } else { + } else if (rl->state == isc_ratelimiter_worklimited) { result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, &rl->interval, ISC_FALSE); if (result == ISC_R_SUCCESS) rl->state = isc_ratelimiter_ratelimited; + } else { + INSIST(rl->state == isc_ratelimiter_shuttingdown); + result = ISC_R_SHUTTINGDOWN; } UNLOCK(&rl->lock); if (*eventp != NULL) @@ -116,7 +142,7 @@ ratelimiter_tick(isc_task_t *task, isc_event_t *event) { isc_result_t result = ISC_R_SUCCESS; isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; isc_event_t *p; - (void) task; /* Unused */ + UNUSED(task); LOCK(&rl->lock); p = ISC_LIST_HEAD(rl->pending); if (p != NULL) { @@ -147,17 +173,31 @@ ratelimiter_tick(isc_task_t *task, isc_event_t *event) { } void -isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp) { - isc_ratelimiter_t *rl = *ratelimiterp; - isc_event_t *p; +isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { + isc_event_t *ev; + LOCK(&rl->lock); + rl->state = isc_ratelimiter_shuttingdown; (void) isc_timer_reset(rl->timer, isc_timertype_inactive, NULL, NULL, ISC_FALSE); - isc_timer_detach(&rl->timer); - while ((p = ISC_LIST_HEAD(rl->pending)) != NULL) { - ISC_LIST_UNLINK(rl->pending, p, ev_link); - isc_event_free(&p); + while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) { + ISC_LIST_UNLINK(rl->pending, ev, ev_link); + ev->ev_attributes |= ISC_EVENTATTR_CANCELED; + isc_task_send(rl->task, &ev); } - isc_mutex_destroy(&rl->lock); - isc_mem_put(rl->mctx, rl, sizeof(*rl)); + UNLOCK(&rl->lock); +} + +void +isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp) +{ + isc_ratelimiter_t *rl = *ratelimiterp; + isc_event_t *ev = &rl->shutdownevent; + isc_timer_detach(&rl->timer); + /* + * Send an event to our task and wait for it to be delivered + * before freeing memory. This guarantees that any timer + * event still in the task's queue are delivered first. + */ + isc_task_send(rl->task, &ev); *ratelimiterp = NULL; }