ratelimiter.c revision 135446
1135446Strhodes/* 2135446Strhodes * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC") 3135446Strhodes * Copyright (C) 1999-2002 Internet Software Consortium. 4135446Strhodes * 5135446Strhodes * Permission to use, copy, modify, and distribute this software for any 6135446Strhodes * purpose with or without fee is hereby granted, provided that the above 7135446Strhodes * copyright notice and this permission notice appear in all copies. 8135446Strhodes * 9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH 10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 11135446Strhodes * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, 12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE 14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 15135446Strhodes * PERFORMANCE OF THIS SOFTWARE. 16135446Strhodes */ 17135446Strhodes 18135446Strhodes/* $Id: ratelimiter.c,v 1.18.14.4 2004/03/08 09:04:50 marka Exp $ */ 19135446Strhodes 20135446Strhodes#include <config.h> 21135446Strhodes 22135446Strhodes#include <isc/mem.h> 23135446Strhodes#include <isc/ratelimiter.h> 24135446Strhodes#include <isc/task.h> 25135446Strhodes#include <isc/time.h> 26135446Strhodes#include <isc/timer.h> 27135446Strhodes#include <isc/util.h> 28135446Strhodes 29135446Strhodestypedef enum { 30135446Strhodes isc_ratelimiter_stalled = 0, 31135446Strhodes isc_ratelimiter_ratelimited = 1, 32135446Strhodes isc_ratelimiter_idle = 2, 33135446Strhodes isc_ratelimiter_shuttingdown = 3 34135446Strhodes} isc_ratelimiter_state_t; 35135446Strhodes 36135446Strhodesstruct isc_ratelimiter { 37135446Strhodes isc_mem_t * mctx; 38135446Strhodes isc_mutex_t lock; 39135446Strhodes int refs; 40135446Strhodes isc_task_t * task; 41135446Strhodes isc_timer_t * timer; 42135446Strhodes isc_interval_t interval; 43135446Strhodes isc_uint32_t pertic; 44135446Strhodes isc_ratelimiter_state_t state; 45135446Strhodes isc_event_t shutdownevent; 46135446Strhodes ISC_LIST(isc_event_t) pending; 47135446Strhodes}; 48135446Strhodes 49135446Strhodes#define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1) 50135446Strhodes 51135446Strhodesstatic void 52135446Strhodesratelimiter_tick(isc_task_t *task, isc_event_t *event); 53135446Strhodes 54135446Strhodesstatic void 55135446Strhodesratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event); 56135446Strhodes 57135446Strhodesisc_result_t 58135446Strhodesisc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr, 59135446Strhodes isc_task_t *task, isc_ratelimiter_t **ratelimiterp) 60135446Strhodes{ 61135446Strhodes isc_result_t result; 62135446Strhodes isc_ratelimiter_t *rl; 63135446Strhodes INSIST(ratelimiterp != NULL && *ratelimiterp == NULL); 64135446Strhodes 65135446Strhodes rl = isc_mem_get(mctx, sizeof(*rl)); 66135446Strhodes if (rl == NULL) 67135446Strhodes return ISC_R_NOMEMORY; 68135446Strhodes rl->mctx = mctx; 69135446Strhodes rl->refs = 1; 70135446Strhodes rl->task = task; 71135446Strhodes isc_interval_set(&rl->interval, 0, 0); 72135446Strhodes rl->timer = NULL; 73135446Strhodes rl->pertic = 1; 74135446Strhodes rl->state = isc_ratelimiter_idle; 75135446Strhodes ISC_LIST_INIT(rl->pending); 76135446Strhodes 77135446Strhodes result = isc_mutex_init(&rl->lock); 78135446Strhodes if (result != ISC_R_SUCCESS) 79135446Strhodes goto free_mem; 80135446Strhodes result = isc_timer_create(timermgr, isc_timertype_inactive, 81135446Strhodes NULL, NULL, rl->task, ratelimiter_tick, 82135446Strhodes rl, &rl->timer); 83135446Strhodes if (result != ISC_R_SUCCESS) 84135446Strhodes goto free_mutex; 85135446Strhodes 86135446Strhodes /* 87135446Strhodes * Increment the reference count to indicate that we may 88135446Strhodes * (soon) have events outstanding. 89135446Strhodes */ 90135446Strhodes rl->refs++; 91135446Strhodes 92135446Strhodes ISC_EVENT_INIT(&rl->shutdownevent, 93135446Strhodes sizeof(isc_event_t), 94135446Strhodes 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN, 95135446Strhodes ratelimiter_shutdowncomplete, rl, rl, NULL, NULL); 96135446Strhodes 97135446Strhodes *ratelimiterp = rl; 98135446Strhodes return (ISC_R_SUCCESS); 99135446Strhodes 100135446Strhodesfree_mutex: 101135446Strhodes DESTROYLOCK(&rl->lock); 102135446Strhodesfree_mem: 103135446Strhodes isc_mem_put(mctx, rl, sizeof(*rl)); 104135446Strhodes return (result); 105135446Strhodes} 106135446Strhodes 107135446Strhodesisc_result_t 108135446Strhodesisc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) { 109135446Strhodes isc_result_t result = ISC_R_SUCCESS; 110135446Strhodes LOCK(&rl->lock); 111135446Strhodes rl->interval = *interval; 112135446Strhodes /* 113135446Strhodes * If the timer is currently running, change its rate. 114135446Strhodes */ 115135446Strhodes if (rl->state == isc_ratelimiter_ratelimited) { 116135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 117135446Strhodes &rl->interval, ISC_FALSE); 118135446Strhodes } 119135446Strhodes UNLOCK(&rl->lock); 120135446Strhodes return (result); 121135446Strhodes} 122135446Strhodes 123135446Strhodesvoid 124135446Strhodesisc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) { 125135446Strhodes if (pertic == 0) 126135446Strhodes pertic = 1; 127135446Strhodes rl->pertic = pertic; 128135446Strhodes} 129135446Strhodes 130135446Strhodesisc_result_t 131135446Strhodesisc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task, 132135446Strhodes isc_event_t **eventp) 133135446Strhodes{ 134135446Strhodes isc_result_t result = ISC_R_SUCCESS; 135135446Strhodes isc_event_t *ev; 136135446Strhodes 137135446Strhodes REQUIRE(eventp != NULL && *eventp != NULL); 138135446Strhodes REQUIRE(task != NULL); 139135446Strhodes ev = *eventp; 140135446Strhodes REQUIRE(ev->ev_sender == NULL); 141135446Strhodes 142135446Strhodes LOCK(&rl->lock); 143135446Strhodes if (rl->state == isc_ratelimiter_ratelimited || 144135446Strhodes rl->state == isc_ratelimiter_stalled) { 145135446Strhodes isc_event_t *ev = *eventp; 146135446Strhodes ev->ev_sender = task; 147135446Strhodes ISC_LIST_APPEND(rl->pending, ev, ev_link); 148135446Strhodes *eventp = NULL; 149135446Strhodes } else if (rl->state == isc_ratelimiter_idle) { 150135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL, 151135446Strhodes &rl->interval, ISC_FALSE); 152135446Strhodes if (result == ISC_R_SUCCESS) { 153135446Strhodes ev->ev_sender = task; 154135446Strhodes rl->state = isc_ratelimiter_ratelimited; 155135446Strhodes } 156135446Strhodes } else { 157135446Strhodes INSIST(rl->state == isc_ratelimiter_shuttingdown); 158135446Strhodes result = ISC_R_SHUTTINGDOWN; 159135446Strhodes } 160135446Strhodes UNLOCK(&rl->lock); 161135446Strhodes if (*eventp != NULL && result == ISC_R_SUCCESS) 162135446Strhodes isc_task_send(task, eventp); 163135446Strhodes return (result); 164135446Strhodes} 165135446Strhodes 166135446Strhodesstatic void 167135446Strhodesratelimiter_tick(isc_task_t *task, isc_event_t *event) { 168135446Strhodes isc_result_t result = ISC_R_SUCCESS; 169135446Strhodes isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 170135446Strhodes isc_event_t *p; 171135446Strhodes isc_uint32_t pertic; 172135446Strhodes 173135446Strhodes UNUSED(task); 174135446Strhodes 175135446Strhodes isc_event_free(&event); 176135446Strhodes 177135446Strhodes pertic = rl->pertic; 178135446Strhodes while (pertic != 0) { 179135446Strhodes pertic--; 180135446Strhodes LOCK(&rl->lock); 181135446Strhodes p = ISC_LIST_HEAD(rl->pending); 182135446Strhodes if (p != NULL) { 183135446Strhodes /* 184135446Strhodes * There is work to do. Let's do it after unlocking. 185135446Strhodes */ 186135446Strhodes ISC_LIST_UNLINK(rl->pending, p, ev_link); 187135446Strhodes } else { 188135446Strhodes /* 189135446Strhodes * No work left to do. Stop the timer so that we don't 190135446Strhodes * waste resources by having it fire periodically. 191135446Strhodes */ 192135446Strhodes result = isc_timer_reset(rl->timer, 193135446Strhodes isc_timertype_inactive, 194135446Strhodes NULL, NULL, ISC_FALSE); 195135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 196135446Strhodes rl->state = isc_ratelimiter_idle; 197135446Strhodes pertic = 0; /* Force the loop to exit. */ 198135446Strhodes } 199135446Strhodes UNLOCK(&rl->lock); 200135446Strhodes if (p != NULL) { 201135446Strhodes isc_task_t *evtask = p->ev_sender; 202135446Strhodes isc_task_send(evtask, &p); 203135446Strhodes } 204135446Strhodes INSIST(p == NULL); 205135446Strhodes } 206135446Strhodes} 207135446Strhodes 208135446Strhodesvoid 209135446Strhodesisc_ratelimiter_shutdown(isc_ratelimiter_t *rl) { 210135446Strhodes isc_event_t *ev; 211135446Strhodes isc_task_t *task; 212135446Strhodes LOCK(&rl->lock); 213135446Strhodes rl->state = isc_ratelimiter_shuttingdown; 214135446Strhodes (void)isc_timer_reset(rl->timer, isc_timertype_inactive, 215135446Strhodes NULL, NULL, ISC_FALSE); 216135446Strhodes while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) { 217135446Strhodes ISC_LIST_UNLINK(rl->pending, ev, ev_link); 218135446Strhodes ev->ev_attributes |= ISC_EVENTATTR_CANCELED; 219135446Strhodes task = ev->ev_sender; 220135446Strhodes isc_task_send(task, &ev); 221135446Strhodes } 222135446Strhodes isc_timer_detach(&rl->timer); 223135446Strhodes /* 224135446Strhodes * Send an event to our task. The delivery of this event 225135446Strhodes * indicates that no more timer events will be delivered. 226135446Strhodes */ 227135446Strhodes ev = &rl->shutdownevent; 228135446Strhodes isc_task_send(rl->task, &ev); 229135446Strhodes 230135446Strhodes UNLOCK(&rl->lock); 231135446Strhodes} 232135446Strhodes 233135446Strhodesstatic void 234135446Strhodesratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) { 235135446Strhodes isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg; 236135446Strhodes 237135446Strhodes UNUSED(task); 238135446Strhodes 239135446Strhodes isc_ratelimiter_detach(&rl); 240135446Strhodes} 241135446Strhodes 242135446Strhodesstatic void 243135446Strhodesratelimiter_free(isc_ratelimiter_t *rl) { 244135446Strhodes DESTROYLOCK(&rl->lock); 245135446Strhodes isc_mem_put(rl->mctx, rl, sizeof(*rl)); 246135446Strhodes} 247135446Strhodes 248135446Strhodesvoid 249135446Strhodesisc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) { 250135446Strhodes REQUIRE(source != NULL); 251135446Strhodes REQUIRE(target != NULL && *target == NULL); 252135446Strhodes 253135446Strhodes LOCK(&source->lock); 254135446Strhodes REQUIRE(source->refs > 0); 255135446Strhodes source->refs++; 256135446Strhodes INSIST(source->refs > 0); 257135446Strhodes UNLOCK(&source->lock); 258135446Strhodes *target = source; 259135446Strhodes} 260135446Strhodes 261135446Strhodesvoid 262135446Strhodesisc_ratelimiter_detach(isc_ratelimiter_t **rlp) { 263135446Strhodes isc_ratelimiter_t *rl = *rlp; 264135446Strhodes isc_boolean_t free_now = ISC_FALSE; 265135446Strhodes 266135446Strhodes LOCK(&rl->lock); 267135446Strhodes REQUIRE(rl->refs > 0); 268135446Strhodes rl->refs--; 269135446Strhodes if (rl->refs == 0) 270135446Strhodes free_now = ISC_TRUE; 271135446Strhodes UNLOCK(&rl->lock); 272135446Strhodes 273135446Strhodes if (free_now) 274135446Strhodes ratelimiter_free(rl); 275135446Strhodes 276135446Strhodes *rlp = NULL; 277135446Strhodes} 278135446Strhodes 279135446Strhodesisc_result_t 280135446Strhodesisc_ratelimiter_stall(isc_ratelimiter_t *rl) { 281135446Strhodes isc_result_t result = ISC_R_SUCCESS; 282135446Strhodes 283135446Strhodes LOCK(&rl->lock); 284135446Strhodes switch (rl->state) { 285135446Strhodes case isc_ratelimiter_shuttingdown: 286135446Strhodes result = ISC_R_SHUTTINGDOWN; 287135446Strhodes break; 288135446Strhodes case isc_ratelimiter_ratelimited: 289135446Strhodes result = isc_timer_reset(rl->timer, isc_timertype_inactive, 290135446Strhodes NULL, NULL, ISC_FALSE); 291135446Strhodes RUNTIME_CHECK(result == ISC_R_SUCCESS); 292135446Strhodes case isc_ratelimiter_idle: 293135446Strhodes case isc_ratelimiter_stalled: 294135446Strhodes rl->state = isc_ratelimiter_stalled; 295135446Strhodes break; 296135446Strhodes } 297135446Strhodes UNLOCK(&rl->lock); 298135446Strhodes return (result); 299135446Strhodes} 300135446Strhodes 301135446Strhodesisc_result_t 302135446Strhodesisc_ratelimiter_release(isc_ratelimiter_t *rl) { 303135446Strhodes isc_result_t result = ISC_R_SUCCESS; 304135446Strhodes 305135446Strhodes LOCK(&rl->lock); 306135446Strhodes switch (rl->state) { 307135446Strhodes case isc_ratelimiter_shuttingdown: 308135446Strhodes result = ISC_R_SHUTTINGDOWN; 309135446Strhodes break; 310135446Strhodes case isc_ratelimiter_stalled: 311135446Strhodes if (!ISC_LIST_EMPTY(rl->pending)) { 312135446Strhodes result = isc_timer_reset(rl->timer, 313135446Strhodes isc_timertype_ticker, NULL, 314135446Strhodes &rl->interval, ISC_FALSE); 315135446Strhodes if (result == ISC_R_SUCCESS) 316135446Strhodes rl->state = isc_ratelimiter_ratelimited; 317135446Strhodes } else 318135446Strhodes rl->state = isc_ratelimiter_idle; 319135446Strhodes break; 320135446Strhodes case isc_ratelimiter_ratelimited: 321135446Strhodes case isc_ratelimiter_idle: 322135446Strhodes break; 323135446Strhodes } 324135446Strhodes UNLOCK(&rl->lock); 325135446Strhodes return (result); 326135446Strhodes} 327