Return to ratelimiter.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / lib / isc |
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: }