Annotation of embedaddon/ntp/lib/isc/ratelimiter.c, revision 1.1.1.1

1.1       misho       1: /*
                      2:  * Copyright (C) 2004, 2005, 2007  Internet Systems Consortium, Inc. ("ISC")
                      3:  * Copyright (C) 1999-2002  Internet Software Consortium.
                      4:  *
                      5:  * Permission to use, copy, modify, and/or distribute this software for any
                      6:  * purpose with or without fee is hereby granted, provided that the above
                      7:  * copyright notice and this permission notice appear in all copies.
                      8:  *
                      9:  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
                     10:  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
                     11:  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
                     12:  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
                     13:  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
                     14:  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
                     15:  * PERFORMANCE OF THIS SOFTWARE.
                     16:  */
                     17: 
                     18: /* $Id: ratelimiter.c,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
                     19: 
                     20: /*! \file */
                     21: 
                     22: #include <config.h>
                     23: 
                     24: #include <isc/mem.h>
                     25: #include <isc/ratelimiter.h>
                     26: #include <isc/task.h>
                     27: #include <isc/time.h>
                     28: #include <isc/timer.h>
                     29: #include <isc/util.h>
                     30: 
                     31: typedef enum {
                     32:        isc_ratelimiter_stalled = 0,
                     33:        isc_ratelimiter_ratelimited = 1,
                     34:        isc_ratelimiter_idle = 2,
                     35:        isc_ratelimiter_shuttingdown = 3
                     36: } isc_ratelimiter_state_t;
                     37: 
                     38: struct isc_ratelimiter {
                     39:        isc_mem_t *             mctx;
                     40:        isc_mutex_t             lock;
                     41:        int                     refs;
                     42:        isc_task_t *            task;
                     43:        isc_timer_t *           timer;
                     44:        isc_interval_t          interval;
                     45:        isc_uint32_t            pertic;
                     46:        isc_ratelimiter_state_t state;
                     47:        isc_event_t             shutdownevent;
                     48:        ISC_LIST(isc_event_t)   pending;
                     49: };
                     50: 
                     51: #define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
                     52: 
                     53: static void
                     54: ratelimiter_tick(isc_task_t *task, isc_event_t *event);
                     55: 
                     56: static void
                     57: ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event);
                     58: 
                     59: isc_result_t
                     60: isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
                     61:                       isc_task_t *task, isc_ratelimiter_t **ratelimiterp)
                     62: {
                     63:        isc_result_t result;
                     64:        isc_ratelimiter_t *rl;
                     65:        INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
                     66: 
                     67:        rl = isc_mem_get(mctx, sizeof(*rl));
                     68:        if (rl == NULL)
                     69:                return ISC_R_NOMEMORY;
                     70:        rl->mctx = mctx;
                     71:        rl->refs = 1;
                     72:        rl->task = task;
                     73:        isc_interval_set(&rl->interval, 0, 0);
                     74:        rl->timer = NULL;
                     75:        rl->pertic = 1;
                     76:        rl->state = isc_ratelimiter_idle;
                     77:        ISC_LIST_INIT(rl->pending);
                     78: 
                     79:        result = isc_mutex_init(&rl->lock);
                     80:        if (result != ISC_R_SUCCESS)
                     81:                goto free_mem;
                     82:        result = isc_timer_create(timermgr, isc_timertype_inactive,
                     83:                                  NULL, NULL, rl->task, ratelimiter_tick,
                     84:                                  rl, &rl->timer);
                     85:        if (result != ISC_R_SUCCESS)
                     86:                goto free_mutex;
                     87: 
                     88:        /*
                     89:         * Increment the reference count to indicate that we may
                     90:         * (soon) have events outstanding.
                     91:         */
                     92:        rl->refs++;
                     93: 
                     94:        ISC_EVENT_INIT(&rl->shutdownevent,
                     95:                       sizeof(isc_event_t),
                     96:                       0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN,
                     97:                       ratelimiter_shutdowncomplete, rl, rl, NULL, NULL);
                     98: 
                     99:        *ratelimiterp = rl;
                    100:        return (ISC_R_SUCCESS);
                    101: 
                    102: free_mutex:
                    103:        DESTROYLOCK(&rl->lock);
                    104: free_mem:
                    105:        isc_mem_put(mctx, rl, sizeof(*rl));
                    106:        return (result);
                    107: }
                    108: 
                    109: isc_result_t
                    110: isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
                    111:        isc_result_t result = ISC_R_SUCCESS;
                    112:        LOCK(&rl->lock);
                    113:        rl->interval = *interval;
                    114:        /*
                    115:         * If the timer is currently running, change its rate.
                    116:         */
                    117:         if (rl->state == isc_ratelimiter_ratelimited) {
                    118:                result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
                    119:                                         &rl->interval, ISC_FALSE);
                    120:        }
                    121:        UNLOCK(&rl->lock);
                    122:        return (result);
                    123: }
                    124: 
                    125: void
                    126: isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) {
                    127:        if (pertic == 0)
                    128:                pertic = 1;
                    129:        rl->pertic = pertic;
                    130: }
                    131: 
                    132: isc_result_t
                    133: isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
                    134:                        isc_event_t **eventp)
                    135: {
                    136:        isc_result_t result = ISC_R_SUCCESS;
                    137:        isc_event_t *ev;
                    138: 
                    139:        REQUIRE(eventp != NULL && *eventp != NULL);
                    140:        REQUIRE(task != NULL);
                    141:        ev = *eventp;
                    142:        REQUIRE(ev->ev_sender == NULL);
                    143: 
                    144:        LOCK(&rl->lock);
                    145:         if (rl->state == isc_ratelimiter_ratelimited ||
                    146:            rl->state == isc_ratelimiter_stalled) {
                    147:                isc_event_t *ev = *eventp;
                    148:                ev->ev_sender = task;
                    149:                 ISC_LIST_APPEND(rl->pending, ev, ev_link);
                    150:                *eventp = NULL;
                    151:         } else if (rl->state == isc_ratelimiter_idle) {
                    152:                result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
                    153:                                         &rl->interval, ISC_FALSE);
                    154:                if (result == ISC_R_SUCCESS) {
                    155:                        ev->ev_sender = task;
                    156:                        rl->state = isc_ratelimiter_ratelimited;
                    157:                }
                    158:        } else {
                    159:                INSIST(rl->state == isc_ratelimiter_shuttingdown);
                    160:                result = ISC_R_SHUTTINGDOWN;
                    161:        }
                    162:        UNLOCK(&rl->lock);
                    163:        if (*eventp != NULL && result == ISC_R_SUCCESS)
                    164:                isc_task_send(task, eventp);
                    165:        return (result);
                    166: }
                    167: 
                    168: static void
                    169: ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
                    170:        isc_result_t result = ISC_R_SUCCESS;
                    171:        isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
                    172:        isc_event_t *p;
                    173:        isc_uint32_t pertic;
                    174: 
                    175:        UNUSED(task);
                    176: 
                    177:        isc_event_free(&event);
                    178: 
                    179:        pertic = rl->pertic;
                    180:         while (pertic != 0) {
                    181:                pertic--;
                    182:                LOCK(&rl->lock);
                    183:                p = ISC_LIST_HEAD(rl->pending);
                    184:                if (p != NULL) {
                    185:                        /*
                    186:                         * There is work to do.  Let's do it after unlocking.
                    187:                         */
                    188:                        ISC_LIST_UNLINK(rl->pending, p, ev_link);
                    189:                } else {
                    190:                        /*
                    191:                         * No work left to do.  Stop the timer so that we don't
                    192:                         * waste resources by having it fire periodically.
                    193:                         */
                    194:                        result = isc_timer_reset(rl->timer,
                    195:                                                 isc_timertype_inactive,
                    196:                                                 NULL, NULL, ISC_FALSE);
                    197:                        RUNTIME_CHECK(result == ISC_R_SUCCESS);
                    198:                        rl->state = isc_ratelimiter_idle;
                    199:                        pertic = 0;     /* Force the loop to exit. */
                    200:                }
                    201:                UNLOCK(&rl->lock);
                    202:                if (p != NULL) {
                    203:                        isc_task_t *evtask = p->ev_sender;
                    204:                        isc_task_send(evtask, &p);
                    205:                }
                    206:                INSIST(p == NULL);
                    207:        }
                    208: }
                    209: 
                    210: void
                    211: isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
                    212:        isc_event_t *ev;
                    213:        isc_task_t *task;
                    214:        LOCK(&rl->lock);
                    215:        rl->state = isc_ratelimiter_shuttingdown;
                    216:        (void)isc_timer_reset(rl->timer, isc_timertype_inactive,
                    217:                              NULL, NULL, ISC_FALSE);
                    218:        while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
                    219:                ISC_LIST_UNLINK(rl->pending, ev, ev_link);
                    220:                ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
                    221:                task = ev->ev_sender;
                    222:                isc_task_send(task, &ev);
                    223:        }
                    224:        isc_timer_detach(&rl->timer);
                    225:        /*
                    226:         * Send an event to our task.  The delivery of this event
                    227:         * indicates that no more timer events will be delivered.
                    228:         */
                    229:        ev = &rl->shutdownevent;
                    230:        isc_task_send(rl->task, &ev);
                    231: 
                    232:        UNLOCK(&rl->lock);
                    233: }
                    234: 
                    235: static void
                    236: ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) {
                    237:        isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
                    238: 
                    239:        UNUSED(task);
                    240: 
                    241:        isc_ratelimiter_detach(&rl);
                    242: }
                    243: 
                    244: static void
                    245: ratelimiter_free(isc_ratelimiter_t *rl) {
                    246:        DESTROYLOCK(&rl->lock);
                    247:        isc_mem_put(rl->mctx, rl, sizeof(*rl));
                    248: }
                    249: 
                    250: void
                    251: isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) {
                    252:        REQUIRE(source != NULL);
                    253:        REQUIRE(target != NULL && *target == NULL);
                    254: 
                    255:        LOCK(&source->lock);
                    256:        REQUIRE(source->refs > 0);
                    257:        source->refs++;
                    258:        INSIST(source->refs > 0);
                    259:        UNLOCK(&source->lock);
                    260:        *target = source;
                    261: }
                    262: 
                    263: void
                    264: isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
                    265:        isc_ratelimiter_t *rl = *rlp;
                    266:        isc_boolean_t free_now = ISC_FALSE;
                    267: 
                    268:        LOCK(&rl->lock);
                    269:        REQUIRE(rl->refs > 0);
                    270:        rl->refs--;
                    271:        if (rl->refs == 0)
                    272:                free_now = ISC_TRUE;
                    273:        UNLOCK(&rl->lock);
                    274: 
                    275:        if (free_now)
                    276:                ratelimiter_free(rl);
                    277: 
                    278:        *rlp = NULL;
                    279: }
                    280: 
                    281: isc_result_t
                    282: isc_ratelimiter_stall(isc_ratelimiter_t *rl) {
                    283:        isc_result_t result = ISC_R_SUCCESS;
                    284: 
                    285:        LOCK(&rl->lock);
                    286:        switch (rl->state) {
                    287:        case isc_ratelimiter_shuttingdown:
                    288:                result = ISC_R_SHUTTINGDOWN;
                    289:                break;
                    290:        case isc_ratelimiter_ratelimited:
                    291:                result = isc_timer_reset(rl->timer, isc_timertype_inactive,
                    292:                                         NULL, NULL, ISC_FALSE);
                    293:                RUNTIME_CHECK(result == ISC_R_SUCCESS);
                    294:        case isc_ratelimiter_idle:
                    295:        case isc_ratelimiter_stalled:
                    296:                rl->state = isc_ratelimiter_stalled;
                    297:                break;
                    298:        }
                    299:        UNLOCK(&rl->lock);
                    300:        return (result);
                    301: }
                    302: 
                    303: isc_result_t
                    304: isc_ratelimiter_release(isc_ratelimiter_t *rl) {
                    305:        isc_result_t result = ISC_R_SUCCESS;
                    306: 
                    307:        LOCK(&rl->lock);
                    308:        switch (rl->state) {
                    309:        case isc_ratelimiter_shuttingdown:
                    310:                result = ISC_R_SHUTTINGDOWN;
                    311:                break;
                    312:        case isc_ratelimiter_stalled:
                    313:                if (!ISC_LIST_EMPTY(rl->pending)) {
                    314:                        result = isc_timer_reset(rl->timer,
                    315:                                                 isc_timertype_ticker, NULL,
                    316:                                                 &rl->interval, ISC_FALSE);
                    317:                        if (result == ISC_R_SUCCESS)
                    318:                                rl->state = isc_ratelimiter_ratelimited;
                    319:                } else 
                    320:                        rl->state = isc_ratelimiter_idle;
                    321:                break;
                    322:        case isc_ratelimiter_ratelimited:
                    323:        case isc_ratelimiter_idle:
                    324:                break;
                    325:        }
                    326:        UNLOCK(&rl->lock);
                    327:        return (result);
                    328: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>