Annotation of embedaddon/ntp/lib/isc/ratelimiter.c, revision 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>