1 /* $NetBSD: ratelimiter.c,v 1.6 2015/07/08 17:28:59 christos Exp $ */
4 * Copyright (C) 2004, 2005, 2007, 2012, 2014, 2015 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
)
85 result
= isc_timer_create(timermgr
, isc_timertype_inactive
,
86 NULL
, NULL
, rl
->task
, ratelimiter_tick
,
88 if (result
!= ISC_R_SUCCESS
)
92 * Increment the reference count to indicate that we may
93 * (soon) have events outstanding.
97 ISC_EVENT_INIT(&rl
->shutdownevent
,
99 0, NULL
, ISC_RATELIMITEREVENT_SHUTDOWN
,
100 ratelimiter_shutdowncomplete
, rl
, rl
, NULL
, NULL
);
103 return (ISC_R_SUCCESS
);
106 DESTROYLOCK(&rl
->lock
);
108 isc_mem_put(mctx
, rl
, sizeof(*rl
));
113 isc_ratelimiter_setinterval(isc_ratelimiter_t
*rl
, isc_interval_t
*interval
) {
114 isc_result_t result
= ISC_R_SUCCESS
;
117 REQUIRE(interval
!= NULL
);
120 rl
->interval
= *interval
;
122 * If the timer is currently running, change its rate.
124 if (rl
->state
== isc_ratelimiter_ratelimited
) {
125 result
= isc_timer_reset(rl
->timer
, isc_timertype_ticker
, NULL
,
126 &rl
->interval
, ISC_FALSE
);
133 isc_ratelimiter_setpertic(isc_ratelimiter_t
*rl
, isc_uint32_t pertic
) {
143 isc_ratelimiter_enqueue(isc_ratelimiter_t
*rl
, isc_task_t
*task
,
144 isc_event_t
**eventp
)
146 isc_result_t result
= ISC_R_SUCCESS
;
150 REQUIRE(task
!= NULL
);
151 REQUIRE(eventp
!= NULL
&& *eventp
!= NULL
);
153 REQUIRE(ev
->ev_sender
== NULL
);
156 if (rl
->state
== isc_ratelimiter_ratelimited
||
157 rl
->state
== isc_ratelimiter_stalled
) {
158 ev
->ev_sender
= task
;
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
;
169 INSIST(rl
->state
== isc_ratelimiter_shuttingdown
);
170 result
= ISC_R_SHUTTINGDOWN
;
173 if (*eventp
!= NULL
&& result
== ISC_R_SUCCESS
)
174 isc_task_send(task
, eventp
);
179 isc_ratelimiter_dequeue(isc_ratelimiter_t
*rl
, isc_event_t
*event
) {
180 isc_result_t result
= ISC_R_SUCCESS
;
183 REQUIRE(event
!= NULL
);
186 if (ISC_LINK_LINKED(event
, ev_link
)) {
187 ISC_LIST_UNLINK(rl
->pending
, event
, ev_link
);
188 event
->ev_sender
= NULL
;
190 result
= ISC_R_NOTFOUND
;
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
;
204 isc_event_free(&event
);
207 while (pertic
!= 0) {
210 p
= ISC_LIST_HEAD(rl
->pending
);
213 * There is work to do. Let's do it after unlocking.
215 ISC_LIST_UNLINK(rl
->pending
, p
, ev_link
);
218 * No work left to do. Stop the timer so that we don't
219 * waste resources by having it fire periodically.
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. */
230 isc_task_t
*evtask
= p
->ev_sender
;
231 isc_task_send(evtask
, &p
);
238 isc_ratelimiter_shutdown(isc_ratelimiter_t
*rl
) {
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
);
254 isc_timer_detach(&rl
->timer
);
257 * Send an event to our task. The delivery of this event
258 * indicates that no more timer events will be delivered.
260 ev
= &rl
->shutdownevent
;
261 isc_task_send(rl
->task
, &ev
);
267 ratelimiter_shutdowncomplete(isc_task_t
*task
, isc_event_t
*event
) {
268 isc_ratelimiter_t
*rl
= (isc_ratelimiter_t
*)event
->ev_arg
;
272 isc_ratelimiter_detach(&rl
);
276 ratelimiter_free(isc_ratelimiter_t
*rl
) {
277 DESTROYLOCK(&rl
->lock
);
278 isc_mem_put(rl
->mctx
, rl
, sizeof(*rl
));
282 isc_ratelimiter_attach(isc_ratelimiter_t
*source
, isc_ratelimiter_t
**target
) {
284 REQUIRE(source
!= NULL
);
285 REQUIRE(target
!= NULL
&& *target
== NULL
);
288 REQUIRE(source
->refs
> 0);
290 INSIST(source
->refs
> 0);
291 UNLOCK(&source
->lock
);
296 isc_ratelimiter_detach(isc_ratelimiter_t
**rlp
) {
297 isc_ratelimiter_t
*rl
;
298 isc_boolean_t free_now
= ISC_FALSE
;
300 REQUIRE(rlp
!= NULL
&& *rlp
!= NULL
);
305 REQUIRE(rl
->refs
> 0);
312 ratelimiter_free(rl
);
318 isc_ratelimiter_stall(isc_ratelimiter_t
*rl
) {
319 isc_result_t result
= ISC_R_SUCCESS
;
325 case isc_ratelimiter_shuttingdown
:
326 result
= ISC_R_SHUTTINGDOWN
;
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
);
333 case isc_ratelimiter_idle
:
334 case isc_ratelimiter_stalled
:
335 rl
->state
= isc_ratelimiter_stalled
;
343 isc_ratelimiter_release(isc_ratelimiter_t
*rl
) {
344 isc_result_t result
= ISC_R_SUCCESS
;
350 case isc_ratelimiter_shuttingdown
:
351 result
= ISC_R_SHUTTINGDOWN
;
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
;
361 rl
->state
= isc_ratelimiter_idle
;
363 case isc_ratelimiter_ratelimited
:
364 case isc_ratelimiter_idle
: