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.1.1.1 2012/05/29 12:08:38 misho 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>