2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 1999-2001 Internet Software Consortium.
5 * Permission to use, copy, modify, and 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.
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.
18 /* $Id: ratelimiter.c,v 1.18.2.1 2004/03/09 06:11:50 marka Exp $ */
23 #include <isc/ratelimiter.h>
26 #include <isc/timer.h>
30 isc_ratelimiter_ratelimited
,
31 isc_ratelimiter_worklimited
,
32 isc_ratelimiter_shuttingdown
33 } isc_ratelimiter_state_t
;
35 struct isc_ratelimiter
{
41 isc_interval_t interval
;
43 isc_ratelimiter_state_t state
;
44 isc_event_t shutdownevent
;
45 ISC_LIST(isc_event_t
) pending
;
48 #define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
51 ratelimiter_tick(isc_task_t
*task
, isc_event_t
*event
);
54 ratelimiter_shutdowncomplete(isc_task_t
*task
, isc_event_t
*event
);
57 isc_ratelimiter_create(isc_mem_t
*mctx
, isc_timermgr_t
*timermgr
,
58 isc_task_t
*task
, isc_ratelimiter_t
**ratelimiterp
)
61 isc_ratelimiter_t
*rl
;
62 INSIST(ratelimiterp
!= NULL
&& *ratelimiterp
== NULL
);
64 rl
= isc_mem_get(mctx
, sizeof(*rl
));
66 return ISC_R_NOMEMORY
;
70 isc_interval_set(&rl
->interval
, 0, 0);
73 rl
->state
= isc_ratelimiter_worklimited
;
74 ISC_LIST_INIT(rl
->pending
);
76 result
= isc_mutex_init(&rl
->lock
);
77 if (result
!= ISC_R_SUCCESS
)
79 result
= isc_timer_create(timermgr
, isc_timertype_inactive
,
80 NULL
, NULL
, rl
->task
, ratelimiter_tick
,
82 if (result
!= ISC_R_SUCCESS
)
86 * Increment the reference count to indicate that we may
87 * (soon) have events outstanding.
91 ISC_EVENT_INIT(&rl
->shutdownevent
,
93 0, NULL
, ISC_RATELIMITEREVENT_SHUTDOWN
,
94 ratelimiter_shutdowncomplete
, rl
, rl
, NULL
, NULL
);
97 return (ISC_R_SUCCESS
);
100 DESTROYLOCK(&rl
->lock
);
102 isc_mem_put(mctx
, rl
, sizeof(*rl
));
107 isc_ratelimiter_setinterval(isc_ratelimiter_t
*rl
, isc_interval_t
*interval
) {
108 isc_result_t result
= ISC_R_SUCCESS
;
110 rl
->interval
= *interval
;
112 * If the timer is currently running, change its rate.
114 if (rl
->state
== isc_ratelimiter_ratelimited
) {
115 result
= isc_timer_reset(rl
->timer
, isc_timertype_ticker
, NULL
,
116 &rl
->interval
, ISC_FALSE
);
123 isc_ratelimiter_setpertic(isc_ratelimiter_t
*rl
, isc_uint32_t pertic
) {
130 isc_ratelimiter_enqueue(isc_ratelimiter_t
*rl
, isc_task_t
*task
,
131 isc_event_t
**eventp
)
133 isc_result_t result
= ISC_R_SUCCESS
;
136 REQUIRE(eventp
!= NULL
&& *eventp
!= NULL
);
137 REQUIRE(task
!= NULL
);
139 REQUIRE(ev
->ev_sender
== NULL
);
142 if (rl
->state
== isc_ratelimiter_ratelimited
) {
143 isc_event_t
*ev
= *eventp
;
144 ev
->ev_sender
= task
;
145 ISC_LIST_APPEND(rl
->pending
, ev
, ev_link
);
147 } else if (rl
->state
== isc_ratelimiter_worklimited
) {
148 result
= isc_timer_reset(rl
->timer
, isc_timertype_ticker
, NULL
,
149 &rl
->interval
, ISC_FALSE
);
150 if (result
== ISC_R_SUCCESS
) {
151 ev
->ev_sender
= task
;
152 rl
->state
= isc_ratelimiter_ratelimited
;
155 INSIST(rl
->state
== isc_ratelimiter_shuttingdown
);
156 result
= ISC_R_SHUTTINGDOWN
;
159 if (*eventp
!= NULL
&& result
== ISC_R_SUCCESS
)
160 isc_task_send(task
, eventp
);
165 ratelimiter_tick(isc_task_t
*task
, isc_event_t
*event
) {
166 isc_result_t result
= ISC_R_SUCCESS
;
167 isc_ratelimiter_t
*rl
= (isc_ratelimiter_t
*)event
->ev_arg
;
173 isc_event_free(&event
);
176 while (pertic
!= 0) {
179 p
= ISC_LIST_HEAD(rl
->pending
);
182 * There is work to do. Let's do it after unlocking.
184 ISC_LIST_UNLINK(rl
->pending
, p
, ev_link
);
187 * No work left to do. Stop the timer so that we don't
188 * waste resources by having it fire periodically.
190 result
= isc_timer_reset(rl
->timer
,
191 isc_timertype_inactive
,
192 NULL
, NULL
, ISC_FALSE
);
193 RUNTIME_CHECK(result
== ISC_R_SUCCESS
);
194 rl
->state
= isc_ratelimiter_worklimited
;
195 pertic
= 0; /* Force the loop to exit. */
199 isc_task_t
*evtask
= p
->ev_sender
;
200 isc_task_send(evtask
, &p
);
207 isc_ratelimiter_shutdown(isc_ratelimiter_t
*rl
) {
211 rl
->state
= isc_ratelimiter_shuttingdown
;
212 (void)isc_timer_reset(rl
->timer
, isc_timertype_inactive
,
213 NULL
, NULL
, ISC_FALSE
);
214 while ((ev
= ISC_LIST_HEAD(rl
->pending
)) != NULL
) {
215 ISC_LIST_UNLINK(rl
->pending
, ev
, ev_link
);
216 ev
->ev_attributes
|= ISC_EVENTATTR_CANCELED
;
217 task
= ev
->ev_sender
;
218 isc_task_send(task
, &ev
);
220 isc_timer_detach(&rl
->timer
);
222 * Send an event to our task. The delivery of this event
223 * indicates that no more timer events will be delivered.
225 ev
= &rl
->shutdownevent
;
226 isc_task_send(rl
->task
, &ev
);
232 ratelimiter_shutdowncomplete(isc_task_t
*task
, isc_event_t
*event
) {
233 isc_ratelimiter_t
*rl
= (isc_ratelimiter_t
*)event
->ev_arg
;
237 isc_ratelimiter_detach(&rl
);
241 ratelimiter_free(isc_ratelimiter_t
*rl
) {
242 DESTROYLOCK(&rl
->lock
);
243 isc_mem_put(rl
->mctx
, rl
, sizeof(*rl
));
247 isc_ratelimiter_attach(isc_ratelimiter_t
*source
, isc_ratelimiter_t
**target
) {
248 REQUIRE(source
!= NULL
);
249 REQUIRE(target
!= NULL
&& *target
== NULL
);
252 REQUIRE(source
->refs
> 0);
254 INSIST(source
->refs
> 0);
255 UNLOCK(&source
->lock
);
260 isc_ratelimiter_detach(isc_ratelimiter_t
**rlp
) {
261 isc_ratelimiter_t
*rl
= *rlp
;
262 isc_boolean_t free_now
= ISC_FALSE
;
265 REQUIRE(rl
->refs
> 0);
272 ratelimiter_free(rl
);