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