2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-31 22:45:39 +00:00

rate limiter now has separate shutdown() and

destroy() functions, and it guarantees that all queued
events are delivered even in the shutdown case
This commit is contained in:
Andreas Gustafsson
2000-04-26 17:10:32 +00:00
parent b5dbab9c3f
commit 16a107c904
3 changed files with 82 additions and 19 deletions

View File

@@ -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] <isc/print.h> does not need <stdarg.h> or <stddef.h> 98. [cleanup] <isc/print.h> does not need <stdarg.h> or <stddef.h>
unless ISC_PLATFORM_NEEDVSNPRINTF is defined. unless ISC_PLATFORM_NEEDVSNPRINTF is defined.

View File

@@ -43,11 +43,6 @@ ISC_LANG_BEGINDECLS
typedef struct isc_ratelimiter isc_ratelimiter_t; typedef struct isc_ratelimiter isc_ratelimiter_t;
typedef enum {
isc_ratelimiter_ratelimited,
isc_ratelimiter_worklimited
} isc_ratelimiter_state_t;
/***** /*****
***** Functions. ***** Functions.
*****/ *****/
@@ -57,7 +52,7 @@ isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
isc_task_t *task, isc_ratelimiter_t **ratelimiterp); isc_task_t *task, isc_ratelimiter_t **ratelimiterp);
/* /*
* Create a rate limiter. It will execute events in the context * 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 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. * Set the mininum interval between event executions.
* The interval value is copied, so the caller need not preserve it. * The interval value is copied, so the caller need not preserve it.
*
* Requires:
* '*interval' is a nonzero interval.
*/ */
isc_result_t 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 * to doing an isc_task_send() to the rate limiter's task, except
* that the execution may be delayed to achieve the desired rate * that the execution may be delayed to achieve the desired rate
* of execution. * 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 void
isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp); isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp);
/* /*
* Destroy a rate limiter. All events that have not yet been * Destroy a rate limiter.
* dispatched to the task are freed immedately.
* Does not destroy the task or events already queued on it. * 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 */ #endif /* ISC_RATELIMITER_H */

View File

@@ -24,6 +24,12 @@
#include <isc/time.h> #include <isc/time.h>
#include <isc/util.h> #include <isc/util.h>
typedef enum {
isc_ratelimiter_ratelimited,
isc_ratelimiter_worklimited,
isc_ratelimiter_shuttingdown
} isc_ratelimiter_state_t;
struct isc_ratelimiter { struct isc_ratelimiter {
isc_mem_t * mctx; isc_mem_t * mctx;
isc_mutex_t lock; isc_mutex_t lock;
@@ -31,11 +37,23 @@ struct isc_ratelimiter {
isc_timer_t * timer; isc_timer_t * timer;
isc_interval_t interval; isc_interval_t interval;
isc_ratelimiter_state_t state; isc_ratelimiter_state_t state;
isc_event_t shutdownevent;
ISC_LIST(isc_event_t) pending; 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_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_result_t
isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
isc_task_t *task, isc_ratelimiter_t **ratelimiterp) 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) if (result != ISC_R_SUCCESS)
goto free_mutex; 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; *ratelimiterp = rl;
return (ISC_R_SUCCESS); 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_event_t *ev = *eventp;
ISC_LIST_APPEND(rl->pending, ev, ev_link); ISC_LIST_APPEND(rl->pending, ev, ev_link);
*eventp = NULL; *eventp = NULL;
} else { } else if (rl->state == isc_ratelimiter_worklimited) {
result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
&rl->interval, ISC_FALSE); &rl->interval, ISC_FALSE);
if (result == ISC_R_SUCCESS) if (result == ISC_R_SUCCESS)
rl->state = isc_ratelimiter_ratelimited; rl->state = isc_ratelimiter_ratelimited;
} else {
INSIST(rl->state == isc_ratelimiter_shuttingdown);
result = ISC_R_SHUTTINGDOWN;
} }
UNLOCK(&rl->lock); UNLOCK(&rl->lock);
if (*eventp != NULL) 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_result_t result = ISC_R_SUCCESS;
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
isc_event_t *p; isc_event_t *p;
(void) task; /* Unused */ UNUSED(task);
LOCK(&rl->lock); LOCK(&rl->lock);
p = ISC_LIST_HEAD(rl->pending); p = ISC_LIST_HEAD(rl->pending);
if (p != NULL) { if (p != NULL) {
@@ -147,17 +173,31 @@ ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
} }
void void
isc_ratelimiter_destroy(isc_ratelimiter_t **ratelimiterp) { isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
isc_ratelimiter_t *rl = *ratelimiterp; isc_event_t *ev;
isc_event_t *p; LOCK(&rl->lock);
rl->state = isc_ratelimiter_shuttingdown;
(void) isc_timer_reset(rl->timer, isc_timertype_inactive, (void) isc_timer_reset(rl->timer, isc_timertype_inactive,
NULL, NULL, ISC_FALSE); NULL, NULL, ISC_FALSE);
isc_timer_detach(&rl->timer); while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
while ((p = ISC_LIST_HEAD(rl->pending)) != NULL) { ISC_LIST_UNLINK(rl->pending, ev, ev_link);
ISC_LIST_UNLINK(rl->pending, p, ev_link); ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
isc_event_free(&p); isc_task_send(rl->task, &ev);
} }
isc_mutex_destroy(&rl->lock); UNLOCK(&rl->lock);
isc_mem_put(rl->mctx, rl, sizeof(*rl)); }
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; *ratelimiterp = NULL;
} }