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