4 * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 1999-2002 Internet Software Consortium.
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.
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.
20 /* Id: ratelimiter.c,v 1.25 2007/06/19 23:47:17 tbox Exp */
27 #include <isc/ratelimiter.h>
30 #include <isc/timer.h>
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
;
40 struct isc_ratelimiter
{
46 isc_interval_t interval
;
48 isc_ratelimiter_state_t state
;
49 isc_event_t shutdownevent
;
50 ISC_LIST(isc_event_t
) pending
;
53 #define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
56 ratelimiter_tick(isc_task_t
*task
, isc_event_t
*event
);
59 ratelimiter_shutdowncomplete(isc_task_t
*task
, isc_event_t
*event
);
62 isc_ratelimiter_create(isc_mem_t
*mctx
, isc_timermgr_t
*timermgr
,
63 isc_task_t
*task
, isc_ratelimiter_t
**ratelimiterp
)
66 isc_ratelimiter_t
*rl
;
67 INSIST(ratelimiterp
!= NULL
&& *ratelimiterp
== NULL
);
69 rl
= isc_mem_get(mctx
, sizeof(*rl
));
71 return ISC_R_NOMEMORY
;
75 isc_interval_set(&rl
->interval
, 0, 0);
78 rl
->state
= isc_ratelimiter_idle
;
79 ISC_LIST_INIT(rl
->pending
);
81 result
= isc_mutex_init(&rl
->lock
);
82 if (result
!= ISC_R_SUCCESS
)
84 result
= isc_timer_create(timermgr
, isc_timertype_inactive
,
85 NULL
, NULL
, rl
->task
, ratelimiter_tick
,
87 if (result
!= ISC_R_SUCCESS
)
91 * Increment the reference count to indicate that we may
92 * (soon) have events outstanding.
96 ISC_EVENT_INIT(&rl
->shutdownevent
,
98 0, NULL
, ISC_RATELIMITEREVENT_SHUTDOWN
,
99 ratelimiter_shutdowncomplete
, rl
, rl
, NULL
, NULL
);
102 return (ISC_R_SUCCESS
);
105 DESTROYLOCK(&rl
->lock
);
107 isc_mem_put(mctx
, rl
, sizeof(*rl
));
112 isc_ratelimiter_setinterval(isc_ratelimiter_t
*rl
, isc_interval_t
*interval
) {
113 isc_result_t result
= ISC_R_SUCCESS
;
115 rl
->interval
= *interval
;
117 * If the timer is currently running, change its rate.
119 if (rl
->state
== isc_ratelimiter_ratelimited
) {
120 result
= isc_timer_reset(rl
->timer
, isc_timertype_ticker
, NULL
,
121 &rl
->interval
, ISC_FALSE
);
128 isc_ratelimiter_setpertic(isc_ratelimiter_t
*rl
, isc_uint32_t pertic
) {
135 isc_ratelimiter_enqueue(isc_ratelimiter_t
*rl
, isc_task_t
*task
,
136 isc_event_t
**eventp
)
138 isc_result_t result
= ISC_R_SUCCESS
;
141 REQUIRE(eventp
!= NULL
&& *eventp
!= NULL
);
142 REQUIRE(task
!= NULL
);
144 REQUIRE(ev
->ev_sender
== NULL
);
147 if (rl
->state
== isc_ratelimiter_ratelimited
||
148 rl
->state
== isc_ratelimiter_stalled
) {
149 isc_event_t
*ev
= *eventp
;
150 ev
->ev_sender
= task
;
151 ISC_LIST_APPEND(rl
->pending
, ev
, ev_link
);
153 } else if (rl
->state
== isc_ratelimiter_idle
) {
154 result
= isc_timer_reset(rl
->timer
, isc_timertype_ticker
, NULL
,
155 &rl
->interval
, ISC_FALSE
);
156 if (result
== ISC_R_SUCCESS
) {
157 ev
->ev_sender
= task
;
158 rl
->state
= isc_ratelimiter_ratelimited
;
161 INSIST(rl
->state
== isc_ratelimiter_shuttingdown
);
162 result
= ISC_R_SHUTTINGDOWN
;
165 if (*eventp
!= NULL
&& result
== ISC_R_SUCCESS
)
166 isc_task_send(task
, eventp
);
171 ratelimiter_tick(isc_task_t
*task
, isc_event_t
*event
) {
172 isc_result_t result
= ISC_R_SUCCESS
;
173 isc_ratelimiter_t
*rl
= (isc_ratelimiter_t
*)event
->ev_arg
;
179 isc_event_free(&event
);
182 while (pertic
!= 0) {
185 p
= ISC_LIST_HEAD(rl
->pending
);
188 * There is work to do. Let's do it after unlocking.
190 ISC_LIST_UNLINK(rl
->pending
, p
, ev_link
);
193 * No work left to do. Stop the timer so that we don't
194 * waste resources by having it fire periodically.
196 result
= isc_timer_reset(rl
->timer
,
197 isc_timertype_inactive
,
198 NULL
, NULL
, ISC_FALSE
);
199 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
200 rl
->state
= isc_ratelimiter_idle
;
201 pertic
= 0; /* Force the loop to exit. */
205 isc_task_t
*evtask
= p
->ev_sender
;
206 isc_task_send(evtask
, &p
);
213 isc_ratelimiter_shutdown(isc_ratelimiter_t
*rl
) {
217 rl
->state
= isc_ratelimiter_shuttingdown
;
218 (void)isc_timer_reset(rl
->timer
, isc_timertype_inactive
,
219 NULL
, NULL
, ISC_FALSE
);
220 while ((ev
= ISC_LIST_HEAD(rl
->pending
)) != NULL
) {
221 ISC_LIST_UNLINK(rl
->pending
, ev
, ev_link
);
222 ev
->ev_attributes
|= ISC_EVENTATTR_CANCELED
;
223 task
= ev
->ev_sender
;
224 isc_task_send(task
, &ev
);
226 isc_timer_detach(&rl
->timer
);
228 * Send an event to our task. The delivery of this event
229 * indicates that no more timer events will be delivered.
231 ev
= &rl
->shutdownevent
;
232 isc_task_send(rl
->task
, &ev
);
238 ratelimiter_shutdowncomplete(isc_task_t
*task
, isc_event_t
*event
) {
239 isc_ratelimiter_t
*rl
= (isc_ratelimiter_t
*)event
->ev_arg
;
243 isc_ratelimiter_detach(&rl
);
247 ratelimiter_free(isc_ratelimiter_t
*rl
) {
248 DESTROYLOCK(&rl
->lock
);
249 isc_mem_put(rl
->mctx
, rl
, sizeof(*rl
));
253 isc_ratelimiter_attach(isc_ratelimiter_t
*source
, isc_ratelimiter_t
**target
) {
254 REQUIRE(source
!= NULL
);
255 REQUIRE(target
!= NULL
&& *target
== NULL
);
258 REQUIRE(source
->refs
> 0);
260 INSIST(source
->refs
> 0);
261 UNLOCK(&source
->lock
);
266 isc_ratelimiter_detach(isc_ratelimiter_t
**rlp
) {
267 isc_ratelimiter_t
*rl
= *rlp
;
268 isc_boolean_t free_now
= ISC_FALSE
;
271 REQUIRE(rl
->refs
> 0);
278 ratelimiter_free(rl
);
284 isc_ratelimiter_stall(isc_ratelimiter_t
*rl
) {
285 isc_result_t result
= ISC_R_SUCCESS
;
289 case isc_ratelimiter_shuttingdown
:
290 result
= ISC_R_SHUTTINGDOWN
;
292 case isc_ratelimiter_ratelimited
:
293 result
= isc_timer_reset(rl
->timer
, isc_timertype_inactive
,
294 NULL
, NULL
, ISC_FALSE
);
295 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
296 case isc_ratelimiter_idle
:
297 case isc_ratelimiter_stalled
:
298 rl
->state
= isc_ratelimiter_stalled
;
306 isc_ratelimiter_release(isc_ratelimiter_t
*rl
) {
307 isc_result_t result
= ISC_R_SUCCESS
;
311 case isc_ratelimiter_shuttingdown
:
312 result
= ISC_R_SHUTTINGDOWN
;
314 case isc_ratelimiter_stalled
:
315 if (!ISC_LIST_EMPTY(rl
->pending
)) {
316 result
= isc_timer_reset(rl
->timer
,
317 isc_timertype_ticker
, NULL
,
318 &rl
->interval
, ISC_FALSE
);
319 if (result
== ISC_R_SUCCESS
)
320 rl
->state
= isc_ratelimiter_ratelimited
;
322 rl
->state
= isc_ratelimiter_idle
;
324 case isc_ratelimiter_ratelimited
:
325 case isc_ratelimiter_idle
: