2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 14:07:59 +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>
unless ISC_PLATFORM_NEEDVSNPRINTF is defined.

View File

@ -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 */

View File

@ -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;
}