isc__ratelimiter_tick(void *arg) {
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)arg;
uint32_t pertic;
- ISC_LIST(isc_rlevent_t) pending;
REQUIRE(VALID_RATELIMITER(rl));
- ISC_LIST_INIT(pending);
-
LOCK(&rl->lock);
REQUIRE(rl->timer != NULL);
pertic = rl->pertic;
while (pertic != 0) {
isc_rlevent_t *rle = ISC_LIST_HEAD(rl->pending);
- if (rle != NULL) {
- /* There is work to do. Let's do it after unlocking. */
- ISC_LIST_UNLINK(rl->pending, rle, link);
- ISC_LIST_APPEND(pending, rle, link);
- } else {
+ if (rle == NULL) {
/*
* We processed all the scheduled work, but there's a
* room for at least one more event (we haven't consumed
rl->state = isc_ratelimiter_idle;
break;
}
+ /*
+ * Unlink and dispatch under the lock: isc_async_run() is a
+ * non-blocking enqueue, so this stays cheap, and once the
+ * link is TOMBSTONEd a concurrent isc_ratelimiter_dequeue()
+ * sees ISC_LINK_LINKED == false and returns ISC_R_NOTFOUND
+ * cleanly instead of racing with our dispatch.
+ */
+ ISC_LIST_UNLINK(rl->pending, rle, link);
+ isc_async_run(rle->loop, rle->cb, rle->arg);
pertic--;
}
}
unlock:
UNLOCK(&rl->lock);
-
- ISC_LIST_FOREACH(pending, rle, link) {
- ISC_LIST_UNLINK(pending, rle, link);
- isc_async_run(rle->loop, rle->cb, rle->arg);
- }
}
void
void
isc_ratelimiter_shutdown(isc_ratelimiter_t *restrict rl) {
- ISC_LIST(isc_rlevent_t) pending;
-
REQUIRE(VALID_RATELIMITER(rl));
- ISC_LIST_INIT(pending);
-
LOCK(&rl->lock);
if (rl->state != isc_ratelimiter_shuttingdown) {
rl->state = isc_ratelimiter_shuttingdown;
- ISC_LIST_MOVE(pending, rl->pending);
+ ISC_LIST_FOREACH(rl->pending, rle, link) {
+ ISC_LIST_UNLINK(rl->pending, rle, link);
+ rle->canceled = true;
+ isc_async_run(rl->loop, rle->cb, rle->arg);
+ }
isc_ratelimiter_ref(rl);
isc_async_run(rl->loop, isc__ratelimiter_doshutdown, rl);
}
UNLOCK(&rl->lock);
-
- ISC_LIST_FOREACH(pending, rle, link) {
- ISC_LIST_UNLINK(pending, rle, link);
- rle->canceled = true;
- isc_async_run(rl->loop, rle->cb, rle->arg);
- }
}
static void