dns_zonemgr_t **zmgrp) {
dns_zonemgr_t *zmgr;
isc_result_t result;
- isc_loop_t *mainloop = isc_loop_main(loopmgr);
+ isc_loop_t *loop = isc_loop_current(loopmgr);
REQUIRE(mctx != NULL);
REQUIRE(loopmgr != NULL);
/* Unreachable lock. */
isc_rwlock_init(&zmgr->urlock, 0, 0);
- result = isc_ratelimiter_create(mainloop, &zmgr->checkdsrl);
- INSIST(result == ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS) {
- goto free_urlock;
- }
-
- result = isc_ratelimiter_create(mainloop, &zmgr->notifyrl);
- INSIST(result == ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS) {
- goto free_checkdsrl;
- }
-
- result = isc_ratelimiter_create(mainloop, &zmgr->refreshrl);
- INSIST(result == ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS) {
- goto free_notifyrl;
- }
-
- result = isc_ratelimiter_create(mainloop, &zmgr->startupnotifyrl);
- INSIST(result == ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS) {
- goto free_refreshrl;
- }
-
- result = isc_ratelimiter_create(mainloop, &zmgr->startuprefreshrl);
- INSIST(result == ISC_R_SUCCESS);
- if (result != ISC_R_SUCCESS) {
- goto free_startupnotifyrl;
- }
+ isc_ratelimiter_create(loop, &zmgr->checkdsrl);
+ isc_ratelimiter_create(loop, &zmgr->notifyrl);
+ isc_ratelimiter_create(loop, &zmgr->refreshrl);
+ isc_ratelimiter_create(loop, &zmgr->startupnotifyrl);
+ isc_ratelimiter_create(loop, &zmgr->startuprefreshrl);
zmgr->zonetasks = isc_mem_get(
zmgr->mctx, zmgr->workers * sizeof(zmgr->zonetasks[0]));
isc_ratelimiter_shutdown(zmgr->startuprefreshrl);
isc_ratelimiter_detach(&zmgr->startuprefreshrl);
-free_startupnotifyrl:
isc_ratelimiter_shutdown(zmgr->startupnotifyrl);
isc_ratelimiter_detach(&zmgr->startupnotifyrl);
-free_refreshrl:
isc_ratelimiter_shutdown(zmgr->refreshrl);
isc_ratelimiter_detach(&zmgr->refreshrl);
-free_notifyrl:
isc_ratelimiter_shutdown(zmgr->notifyrl);
isc_ratelimiter_detach(&zmgr->notifyrl);
-free_checkdsrl:
isc_ratelimiter_shutdown(zmgr->checkdsrl);
isc_ratelimiter_detach(&zmgr->checkdsrl);
-free_urlock:
+
isc_rwlock_destroy(&zmgr->urlock);
isc_rwlock_destroy(&zmgr->rwlock);
isc_mem_put(zmgr->mctx, zmgr, sizeof(*zmgr));
};
static void
-ratelimiter_tick(void *arg);
+isc__ratelimiter_tick(void *arg);
-isc_result_t
-isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **ratelimiterp) {
+static void
+isc__ratelimiter_start(void *arg);
+
+static void
+isc__ratelimiter_doshutdown(void *arg);
+
+void
+isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **rlp) {
isc_ratelimiter_t *rl = NULL;
isc_mem_t *mctx;
- INSIST(loop != NULL);
- INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
+ REQUIRE(loop != NULL);
+ REQUIRE(rlp != NULL && *rlp == NULL);
mctx = isc_loop_getmctx(loop);
isc_interval_set(&rl->interval, 0, 0);
ISC_LIST_INIT(rl->pending);
+ isc_timer_create(rl->loop, isc__ratelimiter_tick, rl, &rl->timer);
+
isc_mutex_init(&rl->lock);
- *ratelimiterp = rl;
- return (ISC_R_SUCCESS);
+ *rlp = rl;
}
void
LOCK(&rl->lock);
rl->interval = *interval;
- /*
- * If the timer is currently running, its rate will change during
- * the next tick.
- */
+ /* The interval will be adjusted on the next tick */
UNLOCK(&rl->lock);
}
UNLOCK(&rl->lock);
}
+static void
+isc__ratelimiter_start(void *arg) {
+ isc_ratelimiter_t *rl = arg;
+ isc_interval_t interval;
+
+ REQUIRE(VALID_RATELIMITER(rl));
+
+ LOCK(&rl->lock);
+ switch (rl->state) {
+ case isc_ratelimiter_ratelimited:
+ /* The first tick happens immediately */
+ isc_interval_set(&interval, 0, 0);
+ isc_timer_start(rl->timer, isc_timertype_once, &interval);
+ break;
+ case isc_ratelimiter_shuttingdown:
+ /* The ratelimiter is shutting down */
+ break;
+ case isc_ratelimiter_idle:
+ /*
+ * This could happen if we are changing the interval on the
+ * ratelimiter, but all the events were processed and the timer
+ * was stopped before the new interval could be applied.
+ */
+ break;
+ default:
+ UNREACHABLE();
+ }
+ UNLOCK(&rl->lock);
+ isc_ratelimiter_detach(&rl);
+}
+
isc_result_t
isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
isc_event_t **eventp) {
case isc_ratelimiter_idle:
/* Start the ratelimiter */
isc_ratelimiter_ref(rl);
- isc_async_run(rl->loop, ratelimiter_tick, rl);
+ isc_async_run(rl->loop, isc__ratelimiter_start, rl);
rl->state = isc_ratelimiter_ratelimited;
- /* FALLTHROUGH */
+ FALLTHROUGH;
case isc_ratelimiter_ratelimited:
event->ev_sender = task;
*eventp = NULL;
}
static void
-ratelimiter_tick(void *arg) {
+isc__ratelimiter_tick(void *arg) {
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)arg;
isc_event_t *event;
uint32_t pertic;
- bool do_destroy = false;
ISC_LIST(isc_event_t) pending;
REQUIRE(VALID_RATELIMITER(rl));
ISC_LIST_INIT(pending);
LOCK(&rl->lock);
- if (rl->state == isc_ratelimiter_shuttingdown) {
- UNLOCK(&rl->lock);
- do_destroy = (rl->timer != NULL);
- goto done;
- }
- if (rl->timer == NULL) {
- isc_timer_create(rl->loop, ratelimiter_tick, rl, &rl->timer);
- }
+ REQUIRE(rl->timer != NULL);
- /*
- * If the timer was already running with a different rate,
- * this updates it to the correct one.
- */
- isc_timer_start(rl->timer, isc_timertype_ticker, &rl->interval);
+ if (rl->state == isc_ratelimiter_shuttingdown) {
+ INSIST(EMPTY(rl->pending));
+ goto unlock;
+ }
pertic = rl->pertic;
while (pertic != 0) {
- pertic--;
event = ISC_LIST_HEAD(rl->pending);
if (event != NULL) {
/* There is work to do. Let's do it after unlocking. */
ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
ISC_LIST_APPEND(pending, event, ev_ratelink);
} else {
- /* There's no more work to do, destroy the timer */
- do_destroy = true;
+ /*
+ * We processed all the scheduled work, but there's a
+ * room for at least one more event (we haven't consumed
+ * all of the "pertick"), so we can stop the ratelimiter
+ * now, and don't worry about isc_ratelimiter_enqueue()
+ * sending an extra event immediately.
+ */
rl->state = isc_ratelimiter_idle;
break;
}
+ pertic--;
+ }
+
+ if (rl->state != isc_ratelimiter_idle) {
+ /* Reschedule the timer */
+ isc_timer_start(rl->timer, isc_timertype_once, &rl->interval);
}
+unlock:
UNLOCK(&rl->lock);
while ((event = ISC_LIST_HEAD(pending)) != NULL) {
ISC_LIST_UNLINK(pending, event, ev_ratelink);
isc_task_send(event->ev_sender, &event);
}
+}
-done:
- /* No work left to do. Stop and destroy the timer. */
- if (do_destroy) {
- isc_timer_destroy(&rl->timer);
- isc_ratelimiter_detach(&rl);
- }
+void
+isc__ratelimiter_doshutdown(void *arg) {
+ isc_ratelimiter_t *rl = arg;
+
+ REQUIRE(VALID_RATELIMITER(rl));
+
+ LOCK(&rl->lock);
+ INSIST(rl->state == isc_ratelimiter_shuttingdown);
+ INSIST(EMPTY(rl->pending));
+
+ isc_timer_stop(rl->timer);
+ isc_timer_destroy(&rl->timer);
+ isc_loop_detach(&rl->loop);
+ UNLOCK(&rl->lock);
+ isc_ratelimiter_detach(&rl);
}
void
isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
isc_event_t *event;
+ ISC_LIST(isc_event_t) pending;
REQUIRE(VALID_RATELIMITER(rl));
+ ISC_LIST_INIT(pending);
+
LOCK(&rl->lock);
if (rl->state != isc_ratelimiter_shuttingdown) {
rl->state = isc_ratelimiter_shuttingdown;
-
- while ((event = ISC_LIST_HEAD(rl->pending)) != NULL) {
- ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
- event->ev_attributes |= ISC_EVENTATTR_CANCELED;
- isc_task_send(event->ev_sender, &event);
- }
- isc_loop_detach(&rl->loop);
+ ISC_LIST_MOVE(pending, rl->pending);
+ isc_ratelimiter_ref(rl);
+ isc_async_run(rl->loop, isc__ratelimiter_doshutdown, rl);
}
UNLOCK(&rl->lock);
+
+ while ((event = ISC_LIST_HEAD(pending)) != NULL) {
+ ISC_LIST_UNLINK(pending, event, ev_ratelink);
+ event->ev_attributes |= ISC_EVENTATTR_CANCELED;
+ isc_task_send(event->ev_sender, &event);
+ }
}
static void