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:
parent
b5dbab9c3f
commit
16a107c904
4
CHANGES
4
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] <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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user