"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/lib/isc/ratelimiter.c" (4 Sep 2020, 8574 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ratelimiter.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.17.4_vs_9.17.5.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /*! \file */
   13 
   14 #include <inttypes.h>
   15 #include <stdbool.h>
   16 
   17 #include <isc/mem.h>
   18 #include <isc/ratelimiter.h>
   19 #include <isc/refcount.h>
   20 #include <isc/task.h>
   21 #include <isc/time.h>
   22 #include <isc/timer.h>
   23 #include <isc/util.h>
   24 
   25 typedef enum {
   26     isc_ratelimiter_stalled = 0,
   27     isc_ratelimiter_ratelimited = 1,
   28     isc_ratelimiter_idle = 2,
   29     isc_ratelimiter_shuttingdown = 3
   30 } isc_ratelimiter_state_t;
   31 
   32 struct isc_ratelimiter {
   33     isc_mem_t *mctx;
   34     isc_mutex_t lock;
   35     isc_refcount_t references;
   36     isc_task_t *task;
   37     isc_timer_t *timer;
   38     isc_interval_t interval;
   39     uint32_t pertic;
   40     bool pushpop;
   41     isc_ratelimiter_state_t state;
   42     isc_event_t shutdownevent;
   43     ISC_LIST(isc_event_t) pending;
   44 };
   45 
   46 #define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
   47 
   48 static void
   49 ratelimiter_tick(isc_task_t *task, isc_event_t *event);
   50 
   51 static void
   52 ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event);
   53 
   54 isc_result_t
   55 isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
   56                isc_task_t *task, isc_ratelimiter_t **ratelimiterp) {
   57     isc_result_t result;
   58     isc_ratelimiter_t *rl;
   59     INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
   60 
   61     rl = isc_mem_get(mctx, sizeof(*rl));
   62     *rl = (isc_ratelimiter_t){
   63         .mctx = mctx,
   64         .task = task,
   65         .pertic = 1,
   66         .state = isc_ratelimiter_idle,
   67     };
   68 
   69     isc_refcount_init(&rl->references, 1);
   70     isc_interval_set(&rl->interval, 0, 0);
   71     ISC_LIST_INIT(rl->pending);
   72 
   73     isc_mutex_init(&rl->lock);
   74 
   75     result = isc_timer_create(timermgr, isc_timertype_inactive, NULL, NULL,
   76                   rl->task, ratelimiter_tick, rl, &rl->timer);
   77     if (result != ISC_R_SUCCESS) {
   78         goto free_mutex;
   79     }
   80 
   81     /*
   82      * Increment the reference count to indicate that we may
   83      * (soon) have events outstanding.
   84      */
   85     isc_refcount_increment(&rl->references);
   86 
   87     ISC_EVENT_INIT(&rl->shutdownevent, sizeof(isc_event_t), 0, NULL,
   88                ISC_RATELIMITEREVENT_SHUTDOWN,
   89                ratelimiter_shutdowncomplete, rl, rl, NULL, NULL);
   90 
   91     *ratelimiterp = rl;
   92     return (ISC_R_SUCCESS);
   93 
   94 free_mutex:
   95     isc_refcount_decrementz(&rl->references);
   96     isc_refcount_destroy(&rl->references);
   97     isc_mutex_destroy(&rl->lock);
   98     isc_mem_put(mctx, rl, sizeof(*rl));
   99     return (result);
  100 }
  101 
  102 isc_result_t
  103 isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
  104     isc_result_t result = ISC_R_SUCCESS;
  105 
  106     REQUIRE(rl != NULL);
  107     REQUIRE(interval != NULL);
  108 
  109     LOCK(&rl->lock);
  110     rl->interval = *interval;
  111     /*
  112      * If the timer is currently running, change its rate.
  113      */
  114     if (rl->state == isc_ratelimiter_ratelimited) {
  115         result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
  116                      &rl->interval, false);
  117     }
  118     UNLOCK(&rl->lock);
  119     return (result);
  120 }
  121 
  122 void
  123 isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, uint32_t pertic) {
  124     REQUIRE(rl != NULL);
  125 
  126     if (pertic == 0) {
  127         pertic = 1;
  128     }
  129     rl->pertic = pertic;
  130 }
  131 
  132 void
  133 isc_ratelimiter_setpushpop(isc_ratelimiter_t *rl, bool pushpop) {
  134     REQUIRE(rl != NULL);
  135 
  136     rl->pushpop = pushpop;
  137 }
  138 
  139 isc_result_t
  140 isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
  141             isc_event_t **eventp) {
  142     isc_result_t result = ISC_R_SUCCESS;
  143     isc_event_t *ev;
  144 
  145     REQUIRE(rl != NULL);
  146     REQUIRE(task != NULL);
  147     REQUIRE(eventp != NULL && *eventp != NULL);
  148     ev = *eventp;
  149     REQUIRE(ev->ev_sender == NULL);
  150 
  151     LOCK(&rl->lock);
  152     if (rl->state == isc_ratelimiter_ratelimited ||
  153         rl->state == isc_ratelimiter_stalled)
  154     {
  155         ev->ev_sender = task;
  156         *eventp = NULL;
  157         if (rl->pushpop) {
  158             ISC_LIST_PREPEND(rl->pending, ev, ev_ratelink);
  159         } else {
  160             ISC_LIST_APPEND(rl->pending, ev, ev_ratelink);
  161         }
  162     } else if (rl->state == isc_ratelimiter_idle) {
  163         result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
  164                      &rl->interval, false);
  165         if (result == ISC_R_SUCCESS) {
  166             ev->ev_sender = task;
  167             rl->state = isc_ratelimiter_ratelimited;
  168         }
  169     } else {
  170         INSIST(rl->state == isc_ratelimiter_shuttingdown);
  171         result = ISC_R_SHUTTINGDOWN;
  172     }
  173     UNLOCK(&rl->lock);
  174     if (*eventp != NULL && result == ISC_R_SUCCESS) {
  175         isc_task_send(task, eventp);
  176     }
  177     return (result);
  178 }
  179 
  180 isc_result_t
  181 isc_ratelimiter_dequeue(isc_ratelimiter_t *rl, isc_event_t *event) {
  182     isc_result_t result = ISC_R_SUCCESS;
  183 
  184     REQUIRE(rl != NULL);
  185     REQUIRE(event != NULL);
  186 
  187     LOCK(&rl->lock);
  188     if (ISC_LINK_LINKED(event, ev_ratelink)) {
  189         ISC_LIST_UNLINK(rl->pending, event, ev_ratelink);
  190         event->ev_sender = NULL;
  191     } else {
  192         result = ISC_R_NOTFOUND;
  193     }
  194     UNLOCK(&rl->lock);
  195     return (result);
  196 }
  197 
  198 static void
  199 ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
  200     isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
  201     isc_event_t *p;
  202     uint32_t pertic;
  203 
  204     UNUSED(task);
  205 
  206     isc_event_free(&event);
  207 
  208     pertic = rl->pertic;
  209     while (pertic != 0) {
  210         pertic--;
  211         LOCK(&rl->lock);
  212         p = ISC_LIST_HEAD(rl->pending);
  213         if (p != NULL) {
  214             /*
  215              * There is work to do.  Let's do it after unlocking.
  216              */
  217             ISC_LIST_UNLINK(rl->pending, p, ev_ratelink);
  218         } else {
  219             /*
  220              * No work left to do.  Stop the timer so that we don't
  221              * waste resources by having it fire periodically.
  222              */
  223             isc_result_t result = isc_timer_reset(
  224                 rl->timer, isc_timertype_inactive, NULL, NULL,
  225                 false);
  226             RUNTIME_CHECK(result == ISC_R_SUCCESS);
  227             rl->state = isc_ratelimiter_idle;
  228             pertic = 0; /* Force the loop to exit. */
  229         }
  230         UNLOCK(&rl->lock);
  231         if (p != NULL) {
  232             isc_task_t *evtask = p->ev_sender;
  233             isc_task_send(evtask, &p);
  234         }
  235         INSIST(p == NULL);
  236     }
  237 }
  238 
  239 void
  240 isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
  241     isc_event_t *ev;
  242     isc_task_t *task;
  243 
  244     REQUIRE(rl != NULL);
  245 
  246     LOCK(&rl->lock);
  247     rl->state = isc_ratelimiter_shuttingdown;
  248     (void)isc_timer_reset(rl->timer, isc_timertype_inactive, NULL, NULL,
  249                   false);
  250     while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
  251         task = ev->ev_sender;
  252         ISC_LIST_UNLINK(rl->pending, ev, ev_ratelink);
  253         ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
  254         isc_task_send(task, &ev);
  255     }
  256     task = NULL;
  257     isc_task_attach(rl->task, &task);
  258     isc_timer_detach(&rl->timer);
  259 
  260     /*
  261      * Send an event to our task.  The delivery of this event
  262      * indicates that no more timer events will be delivered.
  263      */
  264     ev = &rl->shutdownevent;
  265     isc_task_send(rl->task, &ev);
  266 
  267     UNLOCK(&rl->lock);
  268 }
  269 
  270 static void
  271 ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) {
  272     isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
  273 
  274     UNUSED(task);
  275 
  276     isc_ratelimiter_detach(&rl);
  277     isc_task_detach(&task);
  278 }
  279 
  280 static void
  281 ratelimiter_free(isc_ratelimiter_t *rl) {
  282     isc_refcount_destroy(&rl->references);
  283     isc_mutex_destroy(&rl->lock);
  284     isc_mem_put(rl->mctx, rl, sizeof(*rl));
  285 }
  286 
  287 void
  288 isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) {
  289     REQUIRE(source != NULL);
  290     REQUIRE(target != NULL && *target == NULL);
  291 
  292     isc_refcount_increment(&source->references);
  293 
  294     *target = source;
  295 }
  296 
  297 void
  298 isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
  299     isc_ratelimiter_t *rl;
  300 
  301     REQUIRE(rlp != NULL && *rlp != NULL);
  302 
  303     rl = *rlp;
  304     *rlp = NULL;
  305 
  306     if (isc_refcount_decrement(&rl->references) == 1) {
  307         ratelimiter_free(rl);
  308     }
  309 }
  310 
  311 isc_result_t
  312 isc_ratelimiter_stall(isc_ratelimiter_t *rl) {
  313     isc_result_t result = ISC_R_SUCCESS;
  314 
  315     REQUIRE(rl != NULL);
  316 
  317     LOCK(&rl->lock);
  318     switch (rl->state) {
  319     case isc_ratelimiter_shuttingdown:
  320         result = ISC_R_SHUTTINGDOWN;
  321         break;
  322     case isc_ratelimiter_ratelimited:
  323         result = isc_timer_reset(rl->timer, isc_timertype_inactive,
  324                      NULL, NULL, false);
  325         RUNTIME_CHECK(result == ISC_R_SUCCESS);
  326     /* FALLTHROUGH */
  327     case isc_ratelimiter_idle:
  328     case isc_ratelimiter_stalled:
  329         rl->state = isc_ratelimiter_stalled;
  330         break;
  331     }
  332     UNLOCK(&rl->lock);
  333     return (result);
  334 }
  335 
  336 isc_result_t
  337 isc_ratelimiter_release(isc_ratelimiter_t *rl) {
  338     isc_result_t result = ISC_R_SUCCESS;
  339 
  340     REQUIRE(rl != NULL);
  341 
  342     LOCK(&rl->lock);
  343     switch (rl->state) {
  344     case isc_ratelimiter_shuttingdown:
  345         result = ISC_R_SHUTTINGDOWN;
  346         break;
  347     case isc_ratelimiter_stalled:
  348         if (!ISC_LIST_EMPTY(rl->pending)) {
  349             result = isc_timer_reset(rl->timer,
  350                          isc_timertype_ticker, NULL,
  351                          &rl->interval, false);
  352             if (result == ISC_R_SUCCESS) {
  353                 rl->state = isc_ratelimiter_ratelimited;
  354             }
  355         } else {
  356             rl->state = isc_ratelimiter_idle;
  357         }
  358         break;
  359     case isc_ratelimiter_ratelimited:
  360     case isc_ratelimiter_idle:
  361         break;
  362     }
  363     UNLOCK(&rl->lock);
  364     return (result);
  365 }