refactor isc_ratelimiter to use loop callbacks

the rate limter now uses loop callbacks rather than task events.
the API for isc_ratelimiter_enqueue() has been changed; we now pass
in a loop, a callback function and a callback argument, and
receive back a rate limiter event object (isc_rlevent_t). it
is no longer necessary for the caller to allocate the event.

the callback argument needs to include a pointer to the rlevent
object so that it can be freed using isc_rlevent_free(), or by
dequeueing.
This commit is contained in:
Evan Hunt 2022-10-25 18:35:41 -07:00
parent d6c3d9e04a
commit 7fd78344e0
5 changed files with 226 additions and 258 deletions

View file

@ -645,7 +645,7 @@ struct dns_notify {
dns_tsigkey_t *key;
dns_transport_t *transport;
ISC_LINK(dns_notify_t) link;
isc_event_t *event;
isc_rlevent_t *rlevent;
};
#define DNS_NOTIFY_NOSOA 0x0001U
@ -665,7 +665,7 @@ struct dns_checkds {
dns_tsigkey_t *key;
dns_transport_t *transport;
ISC_LINK(dns_checkds_t) link;
isc_event_t *event;
isc_rlevent_t *rlevent;
};
/*%
@ -884,7 +884,7 @@ stub_callback(isc_task_t *, isc_event_t *);
static void
queue_soa_query(dns_zone_t *zone);
static void
soa_query(isc_task_t *, isc_event_t *);
soa_query(void *arg);
static void
ns_query(dns_zone_t *zone, dns_rdataset_t *soardataset, dns_stub_t *stub);
static int
@ -898,7 +898,7 @@ checkds_createmessage(dns_zone_t *zone, dns_message_t **messagep);
static void
checkds_done(isc_task_t *task, isc_event_t *event);
static void
checkds_send_toaddr(isc_task_t *task, isc_event_t *event);
checkds_send_toaddr(void *arg);
static void
notify_cancel(dns_zone_t *zone);
static void
@ -911,7 +911,7 @@ notify_createmessage(dns_zone_t *zone, unsigned int flags,
static void
notify_done(isc_task_t *task, isc_event_t *event);
static void
notify_send_toaddr(isc_task_t *task, isc_event_t *event);
notify_send_toaddr(void *arg);
static isc_result_t
zone_dump(dns_zone_t *, bool);
static void
@ -11975,22 +11975,21 @@ requeue:
* not a startup notify, re-enqueue on the normal notify
* ratelimiter.
*/
if (notify->event != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
if (notify->rlevent != NULL && (flags & DNS_NOTIFY_STARTUP) == 0 &&
(notify->flags & DNS_NOTIFY_STARTUP) != 0)
{
zmgr = notify->zone->zmgr;
result = isc_ratelimiter_dequeue(zmgr->startupnotifyrl,
notify->event);
&notify->rlevent);
if (result != ISC_R_SUCCESS) {
return (true);
}
notify->flags &= ~DNS_NOTIFY_STARTUP;
result = isc_ratelimiter_enqueue(notify->zone->zmgr->notifyrl,
notify->zone->task,
&notify->event);
result = isc_ratelimiter_enqueue(
notify->zone->zmgr->notifyrl, notify->zone->loop,
notify_send_toaddr, notify, &notify->rlevent);
if (result != ISC_R_SUCCESS) {
isc_event_free(&notify->event);
return (false);
}
}
@ -12176,31 +12175,16 @@ destroy:
static isc_result_t
notify_send_queue(dns_notify_t *notify, bool startup) {
isc_event_t *e;
isc_result_t result;
INSIST(notify->event == NULL);
e = isc_event_allocate(notify->mctx, NULL, DNS_EVENT_NOTIFYSENDTOADDR,
notify_send_toaddr, notify, sizeof(isc_event_t));
if (startup) {
notify->event = e;
}
e->ev_arg = notify;
e->ev_sender = NULL;
result = isc_ratelimiter_enqueue(
return (isc_ratelimiter_enqueue(
startup ? notify->zone->zmgr->startupnotifyrl
: notify->zone->zmgr->notifyrl,
notify->zone->task, &e);
if (result != ISC_R_SUCCESS) {
isc_event_free(&e);
notify->event = NULL;
}
return (result);
notify->zone->loop, notify_send_toaddr, notify,
&notify->rlevent));
}
static void
notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
dns_notify_t *notify;
notify_send_toaddr(void *arg) {
dns_notify_t *notify = (dns_notify_t *)arg;
isc_result_t result;
dns_message_t *message = NULL;
isc_netaddr_t dstip;
@ -12210,21 +12194,12 @@ notify_send_toaddr(isc_task_t *task, isc_event_t *event) {
unsigned int options, timeout;
bool have_notifysource = false;
notify = event->ev_arg;
REQUIRE(DNS_NOTIFY_VALID(notify));
UNUSED(task);
LOCK_ZONE(notify->zone);
notify->event = NULL;
if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_LOADED) == 0) {
result = ISC_R_CANCELED;
goto cleanup;
}
if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0 ||
if (DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_LOADED) == 0 ||
notify->rlevent->canceled ||
DNS_ZONE_FLAG(notify->zone, DNS_ZONEFLG_EXITING) ||
notify->zone->view->requestmgr == NULL || notify->zone->db == NULL)
{
@ -12350,7 +12325,7 @@ cleanup_message:
dns_message_detach(&message);
cleanup:
UNLOCK_ZONE(notify->zone);
isc_event_free(&event);
isc_rlevent_free(&notify->rlevent);
if (result != ISC_R_SUCCESS) {
notify_destroy(notify, false);
}
@ -13991,11 +13966,15 @@ detach:
return;
}
struct soaquery {
dns_zone_t *zone;
isc_rlevent_t *rlevent;
};
static void
queue_soa_query(dns_zone_t *zone) {
isc_event_t *e;
dns_zone_t *dummy = NULL;
isc_result_t result;
struct soaquery *sq = NULL;
ENTER;
/*
@ -14008,31 +13987,28 @@ queue_soa_query(dns_zone_t *zone) {
return;
}
e = isc_event_allocate(zone->mctx, NULL, DNS_EVENT_ZONE, soa_query,
zone, sizeof(isc_event_t));
sq = isc_mem_get(zone->mctx, sizeof(*sq));
*sq = (struct soaquery){ .zone = NULL };
/*
* Attach so that we won't clean up
* until the event is delivered.
* Attach so that we won't clean up until the event is delivered.
*/
zone_iattach(zone, &dummy);
e->ev_arg = zone;
e->ev_sender = NULL;
result = isc_ratelimiter_enqueue(zone->zmgr->refreshrl, zone->task, &e);
zone_iattach(zone, &sq->zone);
result = isc_ratelimiter_enqueue(zone->zmgr->refreshrl, zone->loop,
soa_query, sq, &sq->rlevent);
if (result != ISC_R_SUCCESS) {
zone_idetach(&dummy);
isc_event_free(&e);
zone_idetach(&sq->zone);
isc_mem_put(zone->mctx, sq, sizeof(*sq));
cancel_refresh(zone);
}
}
static void
soa_query(isc_task_t *task, isc_event_t *event) {
soa_query(void *arg) {
struct soaquery *sq = (struct soaquery *)arg;
dns_zone_t *zone = sq->zone;
isc_result_t result = ISC_R_FAILURE;
dns_message_t *message = NULL;
dns_zone_t *zone = event->ev_arg;
dns_zone_t *dummy = NULL;
isc_netaddr_t primaryip;
dns_tsigkey_t *key = NULL;
dns_transport_t *transport = NULL;
@ -14046,13 +14022,10 @@ soa_query(isc_task_t *task, isc_event_t *event) {
REQUIRE(DNS_ZONE_VALID(zone));
UNUSED(task);
ENTER;
LOCK_ZONE(zone);
if (((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0) ||
DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) ||
if (sq->rlevent->canceled || DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING) ||
zone->view->requestmgr == NULL)
{
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_EXITING)) {
@ -14198,7 +14171,7 @@ again:
}
}
zone_iattach(zone, &dummy);
zone_iattach(zone, &(dns_zone_t *){ NULL });
timeout = 15;
if (DNS_ZONE_FLAG(zone, DNS_ZONEFLG_DIALREFRESH)) {
timeout = 30;
@ -14208,7 +14181,7 @@ again:
NULL, NULL, options, key, timeout * 3, timeout, 2, zone->task,
refresh_callback, zone, &zone->request);
if (result != ISC_R_SUCCESS) {
zone_idetach(&dummy);
zone_idetach(&(dns_zone_t *){ zone });
zone_debuglog(zone, __func__, 1,
"dns_request_create() failed: %s",
isc_result_totext(result));
@ -14237,11 +14210,12 @@ cleanup:
if (cancel) {
cancel_refresh(zone);
}
isc_event_free(&event);
UNLOCK_ZONE(zone);
if (do_queue_xfrin) {
queue_xfrin(zone);
}
isc_rlevent_free(&sq->rlevent);
isc_mem_put(zone->mctx, sq, sizeof(*sq));
dns_zone_idetach(&zone);
return;
@ -19725,8 +19699,6 @@ dnssec_report(const char *format, ...) {
static void
checkds_destroy(dns_checkds_t *checkds, bool locked) {
isc_mem_t *mctx;
REQUIRE(DNS_CHECKDS_VALID(checkds));
dns_zone_log(checkds->zone, ISC_LOG_DEBUG(3),
@ -19759,9 +19731,8 @@ checkds_destroy(dns_checkds_t *checkds, bool locked) {
if (checkds->transport != NULL) {
dns_transport_detach(&checkds->transport);
}
mctx = checkds->mctx;
isc_mem_put(checkds->mctx, checkds, sizeof(*checkds));
isc_mem_detach(&mctx);
INSIST(checkds->rlevent == NULL);
isc_mem_putanddetach(&checkds->mctx, checkds, sizeof(*checkds));
}
static isc_result_t
@ -20173,8 +20144,8 @@ checkds_createmessage(dns_zone_t *zone, dns_message_t **messagep) {
}
static void
checkds_send_toaddr(isc_task_t *task, isc_event_t *event) {
dns_checkds_t *checkds;
checkds_send_toaddr(void *arg) {
dns_checkds_t *checkds = (dns_checkds_t *)arg;
isc_result_t result;
dns_message_t *message = NULL;
isc_netaddr_t dstip;
@ -20183,22 +20154,15 @@ checkds_send_toaddr(isc_task_t *task, isc_event_t *event) {
isc_sockaddr_t src;
unsigned int options, timeout;
bool have_checkdssource = false;
bool canceled = checkds->rlevent->canceled;
checkds = event->ev_arg;
REQUIRE(DNS_CHECKDS_VALID(checkds));
UNUSED(task);
isc_rlevent_free(&checkds->rlevent);
LOCK_ZONE(checkds->zone);
checkds->event = NULL;
if (DNS_ZONE_FLAG(checkds->zone, DNS_ZONEFLG_LOADED) == 0) {
result = ISC_R_CANCELED;
goto cleanup;
}
if ((event->ev_attributes & ISC_EVENTATTR_CANCELED) != 0 ||
if (DNS_ZONE_FLAG(checkds->zone, DNS_ZONEFLG_LOADED) == 0 || canceled ||
DNS_ZONE_FLAG(checkds->zone, DNS_ZONEFLG_EXITING) ||
checkds->zone->view->requestmgr == NULL ||
checkds->zone->db == NULL)
@ -20321,32 +20285,11 @@ cleanup_message:
dns_message_detach(&message);
cleanup:
UNLOCK_ZONE(checkds->zone);
isc_event_free(&event);
if (result != ISC_R_SUCCESS) {
checkds_destroy(checkds, false);
}
}
static isc_result_t
checkds_send_queue(dns_checkds_t *checkds) {
isc_event_t *e;
isc_result_t result;
INSIST(checkds->event == NULL);
e = isc_event_allocate(checkds->mctx, NULL, DNS_EVENT_CHECKDSSENDTOADDR,
checkds_send_toaddr, checkds,
sizeof(isc_event_t));
e->ev_arg = checkds;
e->ev_sender = NULL;
result = isc_ratelimiter_enqueue(checkds->zone->zmgr->checkdsrl,
checkds->zone->task, &e);
if (result != ISC_R_SUCCESS) {
isc_event_free(&e);
checkds->event = NULL;
}
return (result);
}
static void
checkds_send(dns_zone_t *zone) {
dns_view_t *view = dns_zone_getview(zone);
@ -20444,7 +20387,9 @@ checkds_send(dns_zone_t *zone) {
}
ISC_LIST_APPEND(zone->checkds_requests, checkds, link);
result = checkds_send_queue(checkds);
result = isc_ratelimiter_enqueue(
checkds->zone->zmgr->checkdsrl, checkds->zone->loop,
checkds_send_toaddr, checkds, &checkds->rlevent);
if (result != ISC_R_SUCCESS) {
dns_zone_log(zone, ISC_LOG_DEBUG(3),
"checkds: send DS query to "

View file

@ -35,6 +35,15 @@
#include <isc/time.h>
#include <isc/types.h>
struct isc_rlevent {
isc_loop_t *loop;
isc_ratelimiter_t *rl;
bool canceled;
isc_job_cb cb;
void *arg;
ISC_LINK(isc_rlevent_t) link;
};
ISC_LANG_BEGINDECLS
/*****
@ -48,7 +57,8 @@ isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **rlp);
*/
void
isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval);
isc_ratelimiter_setinterval(isc_ratelimiter_t *restrict rl,
const isc_interval_t *const interval);
/*!<
* Set the minimum interval between event executions.
* The interval value is copied, so the caller need not preserve it.
@ -58,45 +68,47 @@ isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval);
*/
void
isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t perint);
isc_ratelimiter_setpertic(isc_ratelimiter_t *restrict rl,
const uint32_t perint);
/*%<
* Set the number of events processed per interval timer tick.
* If 'perint' is zero it is treated as 1.
*/
void
isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop);
isc_ratelimiter_setpushpop(isc_ratelimiter_t *restrict rl, const bool pushpop);
/*%<
* Set / clear the ratelimiter to from push pop mode rather
* first in - first out mode (default).
*/
isc_result_t
isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
isc_event_t **eventp);
isc_ratelimiter_enqueue(isc_ratelimiter_t *restrict rl,
isc_loop_t *restrict loop, isc_job_cb cb, void *arg,
isc_rlevent_t **rlep);
/*%<
* Queue an event for rate-limited execution.
*
* This is similar
* to doing an isc_task_send() to the 'task', except that the
* execution may be delayed to achieve the desired rate of
* This is similar to doing an isc_async_run() to the 'loop', except
* that the execution may be delayed to achieve the desired rate of
* execution.
*
* '(*eventp)->ev_sender' is used to hold the task. The caller
* must ensure that the task exists until the event is delivered.
* '*rlep' will be set to point to an allocated ratelimiter event,
* which can be freed by the caller using isc_rlevent_free() when the
* event fires, or by dequeueing.
*
* Requires:
*\li An interval has been set by calling
* isc_ratelimiter_setinterval().
*
*\li 'task' to be non NULL.
*\li '(*eventp)->ev_sender' to be NULL.
*\li 'rl' is a valid ratelimiter.
*\li 'loop ' is non NULL.
*\li 'rlep' is non NULL and '*rlep' is NULL.
*/
isc_result_t
isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event);
isc_ratelimiter_dequeue(isc_ratelimiter_t *restrict rl,
isc_rlevent_t **rleventp);
/*
* Dequeue a event off the ratelimiter queue.
* Dequeue a event off the ratelimiter queue. If the event has not already
* been posted, it will be freed and '*rleventp' will be set to NULL.
*
* Returns:
* \li ISC_R_NOTFOUND if the event is no longer linked to the rate limiter.
@ -104,19 +116,22 @@ isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event);
*/
void
isc_ratelimiter_shutdown(isc_ratelimiter_t *ratelimiter);
isc_ratelimiter_shutdown(isc_ratelimiter_t *restrict rl);
/*%<
* Shut down a rate limiter.
*
* Ensures:
*\li All events that have not yet been
* dispatched to the task are dispatched immediately with
* the #ISC_EVENTATTR_CANCELED bit set in ev_attributes.
*\li All pending events are dispatched immediately with
* rle->canceled set to true.
*
*\li Further attempts to enqueue events will fail with
* #ISC_R_SHUTTINGDOWN.
*
*\li The rate limiter is no longer attached to its task.
*/
void
isc_rlevent_free(isc_rlevent_t **rlep);
/*%<
* Free the rate limiter event '*rlep'.
*/
ISC_REFCOUNT_DECL(isc_ratelimiter);

View file

@ -71,6 +71,7 @@ typedef struct isc_portset isc_portset_t; /*%< Port Set */
typedef struct isc_quota isc_quota_t; /*%< Quota */
typedef struct isc_ratelimiter isc_ratelimiter_t; /*%< Rate Limiter */
typedef struct isc_region isc_region_t; /*%< Region */
typedef struct isc_rlevent isc_rlevent_t; /*%< Rate Limiter Event */
typedef struct isc_signal isc_signal_t; /*%< Signal handler */
typedef struct isc_sockaddr isc_sockaddr_t; /*%< Socket Address */
typedef ISC_LIST(isc_sockaddr_t) isc_sockaddrlist_t; /*%< Socket Address List

View file

@ -17,7 +17,6 @@
#include <stdbool.h>
#include <isc/async.h>
#include <isc/event.h>
#include <isc/loop.h>
#include <isc/magic.h>
#include <isc/mem.h>
@ -48,7 +47,7 @@ struct isc_ratelimiter {
uint32_t pertic;
bool pushpop;
isc_ratelimiter_state_t state;
ISC_LIST(isc_event_t) pending;
ISC_LIST(isc_rlevent_t) pending;
};
static void
@ -91,7 +90,8 @@ isc_ratelimiter_create(isc_loop_t *loop, isc_ratelimiter_t **rlp) {
}
void
isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
isc_ratelimiter_setinterval(isc_ratelimiter_t *restrict rl,
const isc_interval_t *const interval) {
REQUIRE(VALID_RATELIMITER(rl));
REQUIRE(interval != NULL);
@ -102,7 +102,8 @@ isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
}
void
isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) {
isc_ratelimiter_setpertic(isc_ratelimiter_t *restrict rl,
const uint32_t pertic) {
REQUIRE(VALID_RATELIMITER(rl));
REQUIRE(pertic > 0);
@ -112,7 +113,7 @@ isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) {
}
void
isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop) {
isc_ratelimiter_setpushpop(isc_ratelimiter_t *restrict rl, const bool pushpop) {
REQUIRE(VALID_RATELIMITER(rl));
LOCK(&rl->lock);
@ -152,16 +153,15 @@ isc__ratelimiter_start(void *arg) {
}
isc_result_t
isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
isc_event_t **eventp) {
isc_ratelimiter_enqueue(isc_ratelimiter_t *restrict rl,
isc_loop_t *restrict loop, isc_job_cb cb, void *arg,
isc_rlevent_t **rlep) {
isc_result_t result = ISC_R_SUCCESS;
isc_event_t *event;
isc_rlevent_t *rle = NULL;
REQUIRE(VALID_RATELIMITER(rl));
REQUIRE(task != NULL);
REQUIRE(eventp != NULL && *eventp != NULL);
event = *eventp;
REQUIRE(event->ev_sender == NULL);
REQUIRE(loop != NULL);
REQUIRE(rlep != NULL && *rlep == NULL);
LOCK(&rl->lock);
switch (rl->state) {
@ -175,13 +175,21 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
rl->state = isc_ratelimiter_ratelimited;
FALLTHROUGH;
case isc_ratelimiter_ratelimited:
event->ev_sender = task;
*eventp = NULL;
rle = isc_mem_get(isc_loop_getmctx(loop), sizeof(*rle));
*rle = (isc_rlevent_t){
.cb = cb,
.arg = arg,
.link = ISC_LINK_INITIALIZER,
};
isc_loop_attach(loop, &rle->loop);
isc_ratelimiter_attach(rl, &rle->rl);
if (rl->pushpop) {
ISC_LIST_PREPEND(rl->pending, event, ev_ratelink);
ISC_LIST_PREPEND(rl->pending, rle, link);
} else {
ISC_LIST_APPEND(rl->pending, event, ev_ratelink);
ISC_LIST_APPEND(rl->pending, rle, link);
}
*rlep = rle;
break;
default:
UNREACHABLE();
@ -191,30 +199,29 @@ isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
}
isc_result_t
isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) {
isc_ratelimiter_dequeue(isc_ratelimiter_t *restrict rl, isc_rlevent_t **rlep) {
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(rl != NULL);
REQUIRE(event != NULL);
REQUIRE(rlep != NULL);
LOCK(&rl->lock);
if (ISC_LINK_LINKED(event, ev_ratelink)) {
ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
event->ev_sender = NULL;
if (ISC_LINK_LINKED(*rlep, link)) {
ISC_LIST_UNLINK(rl->pending, *rlep, link);
isc_rlevent_free(rlep);
} else {
result = ISC_R_NOTFOUND;
}
UNLOCK(&rl->lock);
return (result);
}
static void
isc__ratelimiter_tick(void *arg) {
isc_ratelimiter_t *rl = (isc_ratelimiter_t *)arg;
isc_event_t *event;
isc_rlevent_t *rle = NULL;
uint32_t pertic;
ISC_LIST(isc_event_t) pending;
ISC_LIST(isc_rlevent_t) pending;
REQUIRE(VALID_RATELIMITER(rl));
@ -231,11 +238,11 @@ isc__ratelimiter_tick(void *arg) {
pertic = rl->pertic;
while (pertic != 0) {
event = ISC_LIST_HEAD(rl->pending);
if (event != NULL) {
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, event, ev_ratelink);
ISC_LIST_APPEND(pending, event, ev_ratelink);
ISC_LIST_UNLINK(rl->pending, rle, link);
ISC_LIST_APPEND(pending, rle, link);
} else {
/*
* We processed all the scheduled work, but there's a
@ -257,9 +264,9 @@ isc__ratelimiter_tick(void *arg) {
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);
while ((rle = ISC_LIST_HEAD(pending)) != NULL) {
ISC_LIST_UNLINK(pending, rle, link);
isc_async_run(rle->loop, rle->cb, rle->arg);
}
}
@ -281,9 +288,9 @@ isc__ratelimiter_doshutdown(void *arg) {
}
void
isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
isc_event_t *event;
ISC_LIST(isc_event_t) pending;
isc_ratelimiter_shutdown(isc_ratelimiter_t *restrict rl) {
isc_rlevent_t *rle = NULL;
ISC_LIST(isc_rlevent_t) pending;
REQUIRE(VALID_RATELIMITER(rl));
@ -298,15 +305,15 @@ isc_ratelimiter_shutdown(isc_ratelimiter_t *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);
while ((rle = ISC_LIST_HEAD(pending)) != NULL) {
ISC_LIST_UNLINK(pending, rle, link);
rle->canceled = true;
isc_async_run(rl->loop, rle->cb, rle->arg);
}
}
static void
ratelimiter_destroy(isc_ratelimiter_t *rl) {
ratelimiter_destroy(isc_ratelimiter_t *restrict rl) {
isc_refcount_destroy(&rl->references);
LOCK(&rl->lock);
@ -317,4 +324,18 @@ ratelimiter_destroy(isc_ratelimiter_t *rl) {
isc_mem_putanddetach(&rl->mctx, rl, sizeof(*rl));
}
void
isc_rlevent_free(isc_rlevent_t **rlep) {
REQUIRE(rlep != NULL && *rlep != NULL);
isc_rlevent_t *rle = *rlep;
isc_mem_t *mctx = isc_loop_getmctx(rle->loop);
*rlep = NULL;
isc_loop_detach(&rle->loop);
isc_ratelimiter_detach(&rle->rl);
isc_mem_put(mctx, rle, sizeof(*rle));
}
ISC_REFCOUNT_IMPL(isc_ratelimiter, ratelimiter_destroy);

View file

@ -36,25 +36,25 @@
isc_ratelimiter_t *rl = NULL;
typedef struct rlstat {
isc_rlevent_t *event;
} rlstat_t;
ISC_LOOP_TEST_IMPL(ratelimiter_create) {
rl = NULL;
assert_null(rl);
expect_assert_failure(isc_ratelimiter_create(NULL, &rl));
expect_assert_failure(isc_ratelimiter_create(mainloop, NULL));
rl = (isc_ratelimiter_t *)&rl;
expect_assert_failure(isc_ratelimiter_create(mainloop, &rl));
assert_null(rl);
rl = NULL;
isc_ratelimiter_create(mainloop, &rl);
isc_ratelimiter_shutdown(rl);
isc_ratelimiter_detach(&rl);
isc_loopmgr_shutdown(loopmgr);
}
ISC_LOOP_TEST_IMPL(ratelimiter_shutdown) {
rl = NULL;
assert_null(rl);
expect_assert_failure(isc_ratelimiter_shutdown(NULL));
expect_assert_failure(isc_ratelimiter_shutdown(rl));
@ -62,7 +62,7 @@ ISC_LOOP_TEST_IMPL(ratelimiter_shutdown) {
}
ISC_LOOP_TEST_IMPL(ratelimiter_detach) {
rl = NULL;
assert_null(rl);
expect_assert_failure(isc_ratelimiter_detach(NULL));
expect_assert_failure(isc_ratelimiter_detach(&rl));
@ -71,31 +71,28 @@ ISC_LOOP_TEST_IMPL(ratelimiter_detach) {
}
static int ticks = 0;
static isc_task_t *rl_task = NULL;
static isc_time_t start_time;
static isc_time_t tick_time;
static void
tick(isc_task_t *task, isc_event_t *event) {
assert_ptr_equal(task, rl_task);
isc_event_free(&event);
tick(void *arg) {
rlstat_t *rlstat = (rlstat_t *)arg;
isc_rlevent_free(&rlstat->event);
isc_mem_put(mctx, rlstat, sizeof(*rlstat));
ticks++;
assert_int_equal(isc_time_now(&tick_time), ISC_R_SUCCESS);
isc_loopmgr_shutdown(loopmgr);
isc_task_detach(&rl_task);
isc_ratelimiter_shutdown(rl);
isc_ratelimiter_detach(&rl);
isc_loopmgr_shutdown(loopmgr);
}
ISC_LOOP_SETUP_IMPL(ratelimiter_common) {
isc_result_t result = isc_task_create(taskmgr, &rl_task, 0);
assert_int_equal(result, ISC_R_SUCCESS);
rl = NULL;
assert_null(rl);
isc_time_set(&tick_time, 0, 0);
assert_int_equal(isc_time_now(&start_time), ISC_R_SUCCESS);
isc_ratelimiter_create(mainloop, &rl);
@ -110,14 +107,12 @@ ISC_LOOP_TEARDOWN_IMPL(ratelimiter_enqueue) { assert_int_equal(ticks, 1); }
ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_enqueue) {
isc_result_t result;
isc_event_t *event = NULL;
rlstat_t *rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
result = isc_ratelimiter_enqueue(rl, rl_task, &event);
result = isc_ratelimiter_enqueue(rl, mainloop, tick, rlstat,
&rlstat->event);
assert_int_equal(result, ISC_R_SUCCESS);
assert_non_null(rlstat->event);
}
ISC_LOOP_SETUP_IMPL(ratelimiter_enqueue_shutdown) {
@ -130,31 +125,27 @@ ISC_LOOP_TEARDOWN_IMPL(ratelimiter_enqueue_shutdown) {
}
ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_enqueue_shutdown) {
isc_event_t *event = NULL;
rlstat_t *rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
isc_rlevent_t *event = NULL;
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
expect_assert_failure(isc_ratelimiter_enqueue(NULL, rl_task, &event));
expect_assert_failure(isc_ratelimiter_enqueue(rl, NULL, &event));
expect_assert_failure(isc_ratelimiter_enqueue(rl, rl_task, NULL));
expect_assert_failure(
isc_ratelimiter_enqueue(rl, rl_task, &(isc_event_t *){ NULL }));
isc_ratelimiter_enqueue(NULL, mainloop, tick, NULL, &event));
expect_assert_failure(
isc_ratelimiter_enqueue(rl, NULL, tick, NULL, &event));
expect_assert_failure(
isc_ratelimiter_enqueue(rl, mainloop, tick, NULL, NULL));
assert_int_equal(isc_ratelimiter_enqueue(rl, rl_task, &event),
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tick, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
assert_non_null(rlstat->event);
isc_ratelimiter_shutdown(rl);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(isc_ratelimiter_enqueue(rl, rl_task, &event),
ISC_R_SHUTTINGDOWN);
isc_event_free(&event);
assert_int_equal(
isc_ratelimiter_enqueue(rl, mainloop, tick, NULL, &event),
ISC_R_SHUTTINGDOWN);
assert_null(event);
}
ISC_LOOP_SETUP_IMPL(ratelimiter_dequeue) {
@ -163,38 +154,43 @@ ISC_LOOP_SETUP_IMPL(ratelimiter_dequeue) {
}
ISC_LOOP_TEARDOWN_IMPL(ratelimiter_dequeue) { /* */
assert_int_equal(ticks, 1);
assert_int_equal(ticks, 0);
}
ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_dequeue) {
isc_event_t *event = NULL;
rlstat_t *rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
isc_rlevent_t *fake = isc_mem_get(mctx, sizeof(*fake));
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(
isc_ratelimiter_enqueue(rl, rl_task, &(isc_event_t *){ event }),
ISC_R_SUCCESS);
assert_int_equal(isc_ratelimiter_dequeue(rl, event), ISC_R_SUCCESS);
isc_event_free(&event);
assert_null(event);
/* This event didn't get scheduled */
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(isc_ratelimiter_dequeue(rl, event), ISC_R_NOTFOUND);
assert_int_equal(isc_ratelimiter_enqueue(rl, rl_task, &event),
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tick, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
assert_null(event);
assert_int_equal(isc_ratelimiter_dequeue(rl, &rlstat->event),
ISC_R_SUCCESS);
isc_mem_put(mctx, rlstat, sizeof(*rlstat));
/* Set up a mock ratelimiter event that isn't actually scheduled */
*fake = (isc_rlevent_t){ .link = ISC_LINK_INITIALIZER };
isc_loop_attach(mainloop, &fake->loop);
isc_ratelimiter_attach(rl, &fake->rl);
assert_int_equal(isc_ratelimiter_dequeue(rl, &fake), ISC_R_NOTFOUND);
isc_loop_detach(&fake->loop);
isc_ratelimiter_detach(&fake->rl);
isc_mem_put(mctx, fake, sizeof(*fake));
isc_ratelimiter_shutdown(rl);
isc_ratelimiter_detach(&rl);
isc_loopmgr_shutdown(loopmgr);
}
static isc_time_t tock_time;
static void
tock(isc_task_t *task, isc_event_t *event) {
assert_ptr_equal(task, rl_task);
isc_event_free(&event);
tock(void *arg) {
rlstat_t *rlstat = (rlstat_t *)arg;
isc_rlevent_free(&rlstat->event);
isc_mem_put(mctx, rlstat, sizeof(*rlstat));
ticks++;
assert_int_equal(isc_time_now(&tock_time), ISC_R_SUCCESS);
@ -216,7 +212,7 @@ ISC_LOOP_TEARDOWN_IMPL(ratelimiter_pertick_interval) {
}
ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_pertick_interval) {
isc_event_t *event = NULL;
rlstat_t *rlstat = NULL;
isc_interval_t interval;
isc_interval_set(&interval, 1, NS_PER_SEC / 10);
@ -231,18 +227,14 @@ ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_pertick_interval) {
isc_ratelimiter_setpertic(rl, 1);
isc_ratelimiter_setpushpop(rl, false);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tock, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(isc_ratelimiter_enqueue(rl, rl_task, &event),
rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tock, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(isc_ratelimiter_enqueue(rl, rl_task, &event),
rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tick, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
}
@ -259,7 +251,7 @@ ISC_LOOP_TEARDOWN_IMPL(ratelimiter_pushpop) {
}
ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_pushpop) {
isc_event_t *event = NULL;
rlstat_t *rlstat = NULL;
isc_interval_t interval;
isc_interval_set(&interval, 1, NS_PER_SEC / 10);
@ -268,21 +260,15 @@ ISC_LOOP_TEST_SETUP_TEARDOWN_IMPL(ratelimiter_pushpop) {
isc_ratelimiter_setpertic(rl, 2);
isc_ratelimiter_setpushpop(rl, true);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tick, NULL,
sizeof(isc_event_t));
assert_non_null(event);
rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tock, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
assert_int_equal(
isc_ratelimiter_enqueue(rl, rl_task, &(isc_event_t *){ event }),
ISC_R_SUCCESS);
event = isc_event_allocate(mctx, NULL, ISC_TASKEVENT_TEST, tock, NULL,
sizeof(isc_event_t));
assert_non_null(event);
assert_int_equal(
isc_ratelimiter_enqueue(rl, rl_task, &(isc_event_t *){ event }),
ISC_R_SUCCESS);
rlstat = isc_mem_getx(mctx, sizeof(*rlstat), ISC_MEM_ZERO);
assert_int_equal(isc_ratelimiter_enqueue(rl, mainloop, tick, rlstat,
&rlstat->event),
ISC_R_SUCCESS);
}
static int