Commit 16a107c9 authored by Andreas Gustafsson's avatar Andreas Gustafsson
Browse files

rate limiter now has separate shutdown() and

destroy() functions, and it guarantees that all queued
events are delivered even in the shutdown case
parent b5dbab9c
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] <isc/print.h> does not need <stdarg.h> or <stddef.h>
unless ISC_PLATFORM_NEEDVSNPRINTF is defined.
......
......@@ -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 */
......@@ -24,6 +24,12 @@
#include <isc/time.h>
#include <isc/util.h>
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;
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment