xref: /titanic_52/usr/src/lib/libinetutil/common/tq.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
30*7c478bd9Sstevel@tonic-gate #include <limits.h>
31*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
32*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/stropts.h>	/* INFTIM */
35*7c478bd9Sstevel@tonic-gate 
36*7c478bd9Sstevel@tonic-gate #include <libinetutil.h>
37*7c478bd9Sstevel@tonic-gate #include "libinetutil_impl.h"
38*7c478bd9Sstevel@tonic-gate 
39*7c478bd9Sstevel@tonic-gate static iu_timer_node_t	*pending_delete_chain = NULL;
40*7c478bd9Sstevel@tonic-gate 
41*7c478bd9Sstevel@tonic-gate static void		destroy_timer(iu_tq_t *, iu_timer_node_t *);
42*7c478bd9Sstevel@tonic-gate static iu_timer_id_t	get_timer_id(iu_tq_t *);
43*7c478bd9Sstevel@tonic-gate static void		release_timer_id(iu_tq_t *, iu_timer_id_t);
44*7c478bd9Sstevel@tonic-gate 
45*7c478bd9Sstevel@tonic-gate /*
46*7c478bd9Sstevel@tonic-gate  * iu_tq_create(): creates, initializes and returns a timer queue for use
47*7c478bd9Sstevel@tonic-gate  *
48*7c478bd9Sstevel@tonic-gate  *   input: void
49*7c478bd9Sstevel@tonic-gate  *  output: iu_tq_t *: the new timer queue
50*7c478bd9Sstevel@tonic-gate  */
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate iu_tq_t *
53*7c478bd9Sstevel@tonic-gate iu_tq_create(void)
54*7c478bd9Sstevel@tonic-gate {
55*7c478bd9Sstevel@tonic-gate 	return (calloc(1, sizeof (iu_tq_t)));
56*7c478bd9Sstevel@tonic-gate }
57*7c478bd9Sstevel@tonic-gate 
58*7c478bd9Sstevel@tonic-gate /*
59*7c478bd9Sstevel@tonic-gate  * iu_tq_destroy(): destroys an existing timer queue
60*7c478bd9Sstevel@tonic-gate  *
61*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue to destroy
62*7c478bd9Sstevel@tonic-gate  *  output: void
63*7c478bd9Sstevel@tonic-gate  */
64*7c478bd9Sstevel@tonic-gate 
65*7c478bd9Sstevel@tonic-gate void
66*7c478bd9Sstevel@tonic-gate iu_tq_destroy(iu_tq_t *tq)
67*7c478bd9Sstevel@tonic-gate {
68*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t *node, *next_node;
69*7c478bd9Sstevel@tonic-gate 
70*7c478bd9Sstevel@tonic-gate 	for (node = tq->iutq_head; node != NULL; node = next_node) {
71*7c478bd9Sstevel@tonic-gate 		next_node = node->iutn_next;
72*7c478bd9Sstevel@tonic-gate 		destroy_timer(tq, node);
73*7c478bd9Sstevel@tonic-gate 	}
74*7c478bd9Sstevel@tonic-gate 
75*7c478bd9Sstevel@tonic-gate 	free(tq);
76*7c478bd9Sstevel@tonic-gate }
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate /*
79*7c478bd9Sstevel@tonic-gate  * insert_timer(): inserts a timer node into a tq's timer list
80*7c478bd9Sstevel@tonic-gate  *
81*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
82*7c478bd9Sstevel@tonic-gate  *	    iu_timer_node_t *: the timer node to insert into the list
83*7c478bd9Sstevel@tonic-gate  *	    uint64_t: the number of milliseconds before this timer fires
84*7c478bd9Sstevel@tonic-gate  *  output: void
85*7c478bd9Sstevel@tonic-gate  */
86*7c478bd9Sstevel@tonic-gate 
87*7c478bd9Sstevel@tonic-gate static void
88*7c478bd9Sstevel@tonic-gate insert_timer(iu_tq_t *tq, iu_timer_node_t *node, uint64_t msec)
89*7c478bd9Sstevel@tonic-gate {
90*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t	*after = NULL;
91*7c478bd9Sstevel@tonic-gate 
92*7c478bd9Sstevel@tonic-gate 	/*
93*7c478bd9Sstevel@tonic-gate 	 * find the node to insert this new node "after".  we do this
94*7c478bd9Sstevel@tonic-gate 	 * instead of the more intuitive "insert before" because with
95*7c478bd9Sstevel@tonic-gate 	 * the insert before approach, a null `before' node pointer
96*7c478bd9Sstevel@tonic-gate 	 * is overloaded in meaning (it could be null because there
97*7c478bd9Sstevel@tonic-gate 	 * are no items in the list, or it could be null because this
98*7c478bd9Sstevel@tonic-gate 	 * is the last item on the list, which are very different cases).
99*7c478bd9Sstevel@tonic-gate 	 */
100*7c478bd9Sstevel@tonic-gate 
101*7c478bd9Sstevel@tonic-gate 	node->iutn_abs_timeout = gethrtime() + (msec * (NANOSEC / MILLISEC));
102*7c478bd9Sstevel@tonic-gate 
103*7c478bd9Sstevel@tonic-gate 	if (tq->iutq_head != NULL &&
104*7c478bd9Sstevel@tonic-gate 	    tq->iutq_head->iutn_abs_timeout < node->iutn_abs_timeout)
105*7c478bd9Sstevel@tonic-gate 		for (after = tq->iutq_head; after->iutn_next != NULL;
106*7c478bd9Sstevel@tonic-gate 		    after = after->iutn_next)
107*7c478bd9Sstevel@tonic-gate 			if (after->iutn_next->iutn_abs_timeout >
108*7c478bd9Sstevel@tonic-gate 			    node->iutn_abs_timeout)
109*7c478bd9Sstevel@tonic-gate 				break;
110*7c478bd9Sstevel@tonic-gate 
111*7c478bd9Sstevel@tonic-gate 	node->iutn_next = after ? after->iutn_next : tq->iutq_head;
112*7c478bd9Sstevel@tonic-gate 	node->iutn_prev = after;
113*7c478bd9Sstevel@tonic-gate 	if (after == NULL)
114*7c478bd9Sstevel@tonic-gate 		tq->iutq_head = node;
115*7c478bd9Sstevel@tonic-gate 	else
116*7c478bd9Sstevel@tonic-gate 		after->iutn_next = node;
117*7c478bd9Sstevel@tonic-gate 
118*7c478bd9Sstevel@tonic-gate 	if (node->iutn_next != NULL)
119*7c478bd9Sstevel@tonic-gate 		node->iutn_next->iutn_prev = node;
120*7c478bd9Sstevel@tonic-gate }
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate /*
123*7c478bd9Sstevel@tonic-gate  * remove_timer(): removes a timer node from the tq's timer list
124*7c478bd9Sstevel@tonic-gate  *
125*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
126*7c478bd9Sstevel@tonic-gate  *	    iu_timer_node_t *: the timer node to remove from the list
127*7c478bd9Sstevel@tonic-gate  *  output: void
128*7c478bd9Sstevel@tonic-gate  */
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate static void
131*7c478bd9Sstevel@tonic-gate remove_timer(iu_tq_t *tq, iu_timer_node_t *node)
132*7c478bd9Sstevel@tonic-gate {
133*7c478bd9Sstevel@tonic-gate 	if (node->iutn_next != NULL)
134*7c478bd9Sstevel@tonic-gate 		node->iutn_next->iutn_prev = node->iutn_prev;
135*7c478bd9Sstevel@tonic-gate 	if (node->iutn_prev != NULL)
136*7c478bd9Sstevel@tonic-gate 		node->iutn_prev->iutn_next = node->iutn_next;
137*7c478bd9Sstevel@tonic-gate 	else
138*7c478bd9Sstevel@tonic-gate 		tq->iutq_head = node->iutn_next;
139*7c478bd9Sstevel@tonic-gate }
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate /*
142*7c478bd9Sstevel@tonic-gate  * destroy_timer(): destroy a timer node
143*7c478bd9Sstevel@tonic-gate  *
144*7c478bd9Sstevel@tonic-gate  *  input: iu_tq_t *: the timer queue the timer node is associated with
145*7c478bd9Sstevel@tonic-gate  *	   iu_timer_node_t *: the node to free
146*7c478bd9Sstevel@tonic-gate  * output: void
147*7c478bd9Sstevel@tonic-gate  */
148*7c478bd9Sstevel@tonic-gate 
149*7c478bd9Sstevel@tonic-gate static void
150*7c478bd9Sstevel@tonic-gate destroy_timer(iu_tq_t *tq, iu_timer_node_t *node)
151*7c478bd9Sstevel@tonic-gate {
152*7c478bd9Sstevel@tonic-gate 	release_timer_id(tq, node->iutn_timer_id);
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate 	/*
155*7c478bd9Sstevel@tonic-gate 	 * if we're in expire, don't delete the node yet, since it may
156*7c478bd9Sstevel@tonic-gate 	 * still be referencing it (through the expire_next pointers)
157*7c478bd9Sstevel@tonic-gate 	 */
158*7c478bd9Sstevel@tonic-gate 
159*7c478bd9Sstevel@tonic-gate 	if (tq->iutq_in_expire) {
160*7c478bd9Sstevel@tonic-gate 		node->iutn_pending_delete++;
161*7c478bd9Sstevel@tonic-gate 		node->iutn_next = pending_delete_chain;
162*7c478bd9Sstevel@tonic-gate 		pending_delete_chain = node;
163*7c478bd9Sstevel@tonic-gate 	} else
164*7c478bd9Sstevel@tonic-gate 		free(node);
165*7c478bd9Sstevel@tonic-gate 
166*7c478bd9Sstevel@tonic-gate }
167*7c478bd9Sstevel@tonic-gate 
168*7c478bd9Sstevel@tonic-gate /*
169*7c478bd9Sstevel@tonic-gate  * iu_schedule_timer(): creates and inserts a timer in the tq's timer list
170*7c478bd9Sstevel@tonic-gate  *
171*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
172*7c478bd9Sstevel@tonic-gate  *	    uint32_t: the number of seconds before this timer fires
173*7c478bd9Sstevel@tonic-gate  *	    iu_tq_callback_t *: the function to call when the timer fires
174*7c478bd9Sstevel@tonic-gate  *	    void *: an argument to pass to the called back function
175*7c478bd9Sstevel@tonic-gate  *  output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
176*7c478bd9Sstevel@tonic-gate  */
177*7c478bd9Sstevel@tonic-gate 
178*7c478bd9Sstevel@tonic-gate iu_timer_id_t
179*7c478bd9Sstevel@tonic-gate iu_schedule_timer(iu_tq_t *tq, uint32_t sec, iu_tq_callback_t *callback,
180*7c478bd9Sstevel@tonic-gate     void *arg)
181*7c478bd9Sstevel@tonic-gate {
182*7c478bd9Sstevel@tonic-gate 	return (iu_schedule_timer_ms(tq, sec * MILLISEC, callback, arg));
183*7c478bd9Sstevel@tonic-gate }
184*7c478bd9Sstevel@tonic-gate 
185*7c478bd9Sstevel@tonic-gate /*
186*7c478bd9Sstevel@tonic-gate  * iu_schedule_ms_timer(): creates and inserts a timer in the tq's timer list,
187*7c478bd9Sstevel@tonic-gate  *			   using millisecond granularity
188*7c478bd9Sstevel@tonic-gate  *
189*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
190*7c478bd9Sstevel@tonic-gate  *	    uint64_t: the number of milliseconds before this timer fires
191*7c478bd9Sstevel@tonic-gate  *	    iu_tq_callback_t *: the function to call when the timer fires
192*7c478bd9Sstevel@tonic-gate  *	    void *: an argument to pass to the called back function
193*7c478bd9Sstevel@tonic-gate  *  output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
194*7c478bd9Sstevel@tonic-gate  */
195*7c478bd9Sstevel@tonic-gate iu_timer_id_t
196*7c478bd9Sstevel@tonic-gate iu_schedule_timer_ms(iu_tq_t *tq, uint64_t ms, iu_tq_callback_t *callback,
197*7c478bd9Sstevel@tonic-gate     void *arg)
198*7c478bd9Sstevel@tonic-gate {
199*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t	*node = calloc(1, sizeof (iu_timer_node_t));
200*7c478bd9Sstevel@tonic-gate 
201*7c478bd9Sstevel@tonic-gate 	if (node == NULL)
202*7c478bd9Sstevel@tonic-gate 		return (-1);
203*7c478bd9Sstevel@tonic-gate 
204*7c478bd9Sstevel@tonic-gate 	node->iutn_callback	= callback;
205*7c478bd9Sstevel@tonic-gate 	node->iutn_arg	= arg;
206*7c478bd9Sstevel@tonic-gate 	node->iutn_timer_id	= get_timer_id(tq);
207*7c478bd9Sstevel@tonic-gate 	if (node->iutn_timer_id == -1) {
208*7c478bd9Sstevel@tonic-gate 		free(node);
209*7c478bd9Sstevel@tonic-gate 		return (-1);
210*7c478bd9Sstevel@tonic-gate 	}
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	insert_timer(tq, node, ms);
213*7c478bd9Sstevel@tonic-gate 
214*7c478bd9Sstevel@tonic-gate 	return (node->iutn_timer_id);
215*7c478bd9Sstevel@tonic-gate }
216*7c478bd9Sstevel@tonic-gate 
217*7c478bd9Sstevel@tonic-gate /*
218*7c478bd9Sstevel@tonic-gate  * iu_cancel_timer(): cancels a pending timer from a timer queue's timer list
219*7c478bd9Sstevel@tonic-gate  *
220*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
221*7c478bd9Sstevel@tonic-gate  *	    iu_timer_id_t: the timer id returned from iu_schedule_timer
222*7c478bd9Sstevel@tonic-gate  *	    void **: if non-NULL, a place to return the argument passed to
223*7c478bd9Sstevel@tonic-gate  *		     iu_schedule_timer
224*7c478bd9Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
225*7c478bd9Sstevel@tonic-gate  */
226*7c478bd9Sstevel@tonic-gate 
227*7c478bd9Sstevel@tonic-gate int
228*7c478bd9Sstevel@tonic-gate iu_cancel_timer(iu_tq_t *tq, iu_timer_id_t timer_id, void **arg)
229*7c478bd9Sstevel@tonic-gate {
230*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t	*node;
231*7c478bd9Sstevel@tonic-gate 
232*7c478bd9Sstevel@tonic-gate 	if (timer_id == -1)
233*7c478bd9Sstevel@tonic-gate 		return (0);
234*7c478bd9Sstevel@tonic-gate 
235*7c478bd9Sstevel@tonic-gate 	for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
236*7c478bd9Sstevel@tonic-gate 		if (node->iutn_timer_id == timer_id) {
237*7c478bd9Sstevel@tonic-gate 			if (arg != NULL)
238*7c478bd9Sstevel@tonic-gate 				*arg = node->iutn_arg;
239*7c478bd9Sstevel@tonic-gate 			remove_timer(tq, node);
240*7c478bd9Sstevel@tonic-gate 			destroy_timer(tq, node);
241*7c478bd9Sstevel@tonic-gate 			return (1);
242*7c478bd9Sstevel@tonic-gate 		}
243*7c478bd9Sstevel@tonic-gate 	}
244*7c478bd9Sstevel@tonic-gate 	return (0);
245*7c478bd9Sstevel@tonic-gate }
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate /*
248*7c478bd9Sstevel@tonic-gate  * iu_adjust_timer(): adjusts the fire time of a timer in the tq's timer list
249*7c478bd9Sstevel@tonic-gate  *
250*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
251*7c478bd9Sstevel@tonic-gate  *	    iu_timer_id_t: the timer id returned from iu_schedule_timer
252*7c478bd9Sstevel@tonic-gate  *	    uint32_t: the number of seconds before this timer fires
253*7c478bd9Sstevel@tonic-gate  *  output: int: 1 on success, 0 on failure
254*7c478bd9Sstevel@tonic-gate  */
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate int
257*7c478bd9Sstevel@tonic-gate iu_adjust_timer(iu_tq_t *tq, iu_timer_id_t timer_id, uint32_t sec)
258*7c478bd9Sstevel@tonic-gate {
259*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t	*node;
260*7c478bd9Sstevel@tonic-gate 
261*7c478bd9Sstevel@tonic-gate 	if (timer_id == -1)
262*7c478bd9Sstevel@tonic-gate 		return (0);
263*7c478bd9Sstevel@tonic-gate 
264*7c478bd9Sstevel@tonic-gate 	for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
265*7c478bd9Sstevel@tonic-gate 		if (node->iutn_timer_id == timer_id) {
266*7c478bd9Sstevel@tonic-gate 			remove_timer(tq, node);
267*7c478bd9Sstevel@tonic-gate 			insert_timer(tq, node, sec * MILLISEC);
268*7c478bd9Sstevel@tonic-gate 			return (1);
269*7c478bd9Sstevel@tonic-gate 		}
270*7c478bd9Sstevel@tonic-gate 	}
271*7c478bd9Sstevel@tonic-gate 	return (0);
272*7c478bd9Sstevel@tonic-gate }
273*7c478bd9Sstevel@tonic-gate 
274*7c478bd9Sstevel@tonic-gate /*
275*7c478bd9Sstevel@tonic-gate  * iu_earliest_timer(): returns the time until the next timer fires on a tq
276*7c478bd9Sstevel@tonic-gate  *
277*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
278*7c478bd9Sstevel@tonic-gate  *  output: int: the number of milliseconds until the next timer (up to
279*7c478bd9Sstevel@tonic-gate  *	    a maximum value of INT_MAX), or INFTIM if no timers are pending.
280*7c478bd9Sstevel@tonic-gate  */
281*7c478bd9Sstevel@tonic-gate 
282*7c478bd9Sstevel@tonic-gate int
283*7c478bd9Sstevel@tonic-gate iu_earliest_timer(iu_tq_t *tq)
284*7c478bd9Sstevel@tonic-gate {
285*7c478bd9Sstevel@tonic-gate 	unsigned long long	timeout_interval;
286*7c478bd9Sstevel@tonic-gate 	hrtime_t		current_time = gethrtime();
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 	if (tq->iutq_head == NULL)
289*7c478bd9Sstevel@tonic-gate 		return (INFTIM);
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate 	/*
292*7c478bd9Sstevel@tonic-gate 	 * event might've already happened if we haven't gotten a chance to
293*7c478bd9Sstevel@tonic-gate 	 * run in a while; return zero and pretend it just expired.
294*7c478bd9Sstevel@tonic-gate 	 */
295*7c478bd9Sstevel@tonic-gate 
296*7c478bd9Sstevel@tonic-gate 	if (tq->iutq_head->iutn_abs_timeout <= current_time)
297*7c478bd9Sstevel@tonic-gate 		return (0);
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate 	/*
300*7c478bd9Sstevel@tonic-gate 	 * since the timers are ordered in absolute time-to-fire, just
301*7c478bd9Sstevel@tonic-gate 	 * subtract from the head of the list.
302*7c478bd9Sstevel@tonic-gate 	 */
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	timeout_interval =
305*7c478bd9Sstevel@tonic-gate 	    (tq->iutq_head->iutn_abs_timeout - current_time) / 1000000;
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	return (MIN(timeout_interval, INT_MAX));
308*7c478bd9Sstevel@tonic-gate }
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate /*
311*7c478bd9Sstevel@tonic-gate  * iu_expire_timers(): expires all pending timers on a given timer queue
312*7c478bd9Sstevel@tonic-gate  *
313*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
314*7c478bd9Sstevel@tonic-gate  *  output: int: the number of timers expired
315*7c478bd9Sstevel@tonic-gate  */
316*7c478bd9Sstevel@tonic-gate 
317*7c478bd9Sstevel@tonic-gate int
318*7c478bd9Sstevel@tonic-gate iu_expire_timers(iu_tq_t *tq)
319*7c478bd9Sstevel@tonic-gate {
320*7c478bd9Sstevel@tonic-gate 	iu_timer_node_t	*node, *next_node;
321*7c478bd9Sstevel@tonic-gate 	int		n_expired = 0;
322*7c478bd9Sstevel@tonic-gate 	hrtime_t	current_time = gethrtime();
323*7c478bd9Sstevel@tonic-gate 
324*7c478bd9Sstevel@tonic-gate 	/*
325*7c478bd9Sstevel@tonic-gate 	 * in_expire is in the iu_tq_t instead of being passed through as
326*7c478bd9Sstevel@tonic-gate 	 * an argument to remove_timer() below since the callback
327*7c478bd9Sstevel@tonic-gate 	 * function may call iu_cancel_timer() itself as well.
328*7c478bd9Sstevel@tonic-gate 	 */
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 	tq->iutq_in_expire++;
331*7c478bd9Sstevel@tonic-gate 
332*7c478bd9Sstevel@tonic-gate 	/*
333*7c478bd9Sstevel@tonic-gate 	 * this function builds another linked list of timer nodes
334*7c478bd9Sstevel@tonic-gate 	 * through `expire_next' because the normal linked list
335*7c478bd9Sstevel@tonic-gate 	 * may be changed as a result of callbacks canceling and
336*7c478bd9Sstevel@tonic-gate 	 * scheduling timeouts, and thus can't be trusted.
337*7c478bd9Sstevel@tonic-gate 	 */
338*7c478bd9Sstevel@tonic-gate 
339*7c478bd9Sstevel@tonic-gate 	for (node = tq->iutq_head; node != NULL; node = node->iutn_next)
340*7c478bd9Sstevel@tonic-gate 		node->iutn_expire_next = node->iutn_next;
341*7c478bd9Sstevel@tonic-gate 
342*7c478bd9Sstevel@tonic-gate 	for (node = tq->iutq_head; node != NULL;
343*7c478bd9Sstevel@tonic-gate 	    node = node->iutn_expire_next) {
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 		if (node->iutn_abs_timeout > current_time)
346*7c478bd9Sstevel@tonic-gate 			break;
347*7c478bd9Sstevel@tonic-gate 
348*7c478bd9Sstevel@tonic-gate 		/*
349*7c478bd9Sstevel@tonic-gate 		 * fringe condition: two timers fire at the "same
350*7c478bd9Sstevel@tonic-gate 		 * time" (i.e., they're both scheduled called back in
351*7c478bd9Sstevel@tonic-gate 		 * this loop) and one cancels the other.  in this
352*7c478bd9Sstevel@tonic-gate 		 * case, the timer which has already been "cancelled"
353*7c478bd9Sstevel@tonic-gate 		 * should not be called back.
354*7c478bd9Sstevel@tonic-gate 		 */
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 		if (node->iutn_pending_delete)
357*7c478bd9Sstevel@tonic-gate 			continue;
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate 		/*
360*7c478bd9Sstevel@tonic-gate 		 * we remove the timer before calling back the callback
361*7c478bd9Sstevel@tonic-gate 		 * so that a callback which accidentally tries to cancel
362*7c478bd9Sstevel@tonic-gate 		 * itself (through whatever means) doesn't succeed.
363*7c478bd9Sstevel@tonic-gate 		 */
364*7c478bd9Sstevel@tonic-gate 
365*7c478bd9Sstevel@tonic-gate 		n_expired++;
366*7c478bd9Sstevel@tonic-gate 		remove_timer(tq, node);
367*7c478bd9Sstevel@tonic-gate 		destroy_timer(tq, node);
368*7c478bd9Sstevel@tonic-gate 		node->iutn_callback(tq, node->iutn_arg);
369*7c478bd9Sstevel@tonic-gate 	}
370*7c478bd9Sstevel@tonic-gate 
371*7c478bd9Sstevel@tonic-gate 	tq->iutq_in_expire--;
372*7c478bd9Sstevel@tonic-gate 
373*7c478bd9Sstevel@tonic-gate 	/*
374*7c478bd9Sstevel@tonic-gate 	 * any cancels that took place whilst we were expiring timeouts
375*7c478bd9Sstevel@tonic-gate 	 * ended up on the `pending_delete_chain'.  delete them now
376*7c478bd9Sstevel@tonic-gate 	 * that it's safe.
377*7c478bd9Sstevel@tonic-gate 	 */
378*7c478bd9Sstevel@tonic-gate 
379*7c478bd9Sstevel@tonic-gate 	for (node = pending_delete_chain; node != NULL; node = next_node) {
380*7c478bd9Sstevel@tonic-gate 		next_node = node->iutn_next;
381*7c478bd9Sstevel@tonic-gate 		free(node);
382*7c478bd9Sstevel@tonic-gate 	}
383*7c478bd9Sstevel@tonic-gate 	pending_delete_chain = NULL;
384*7c478bd9Sstevel@tonic-gate 
385*7c478bd9Sstevel@tonic-gate 	return (n_expired);
386*7c478bd9Sstevel@tonic-gate }
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate /*
389*7c478bd9Sstevel@tonic-gate  * get_timer_id(): allocates a timer id from the pool
390*7c478bd9Sstevel@tonic-gate  *
391*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
392*7c478bd9Sstevel@tonic-gate  *  output: iu_timer_id_t: the allocated timer id, or -1 if none available
393*7c478bd9Sstevel@tonic-gate  */
394*7c478bd9Sstevel@tonic-gate 
395*7c478bd9Sstevel@tonic-gate static iu_timer_id_t
396*7c478bd9Sstevel@tonic-gate get_timer_id(iu_tq_t *tq)
397*7c478bd9Sstevel@tonic-gate {
398*7c478bd9Sstevel@tonic-gate 	unsigned int	map_index;
399*7c478bd9Sstevel@tonic-gate 	unsigned char	map_bit;
400*7c478bd9Sstevel@tonic-gate 	boolean_t	have_wrapped = B_FALSE;
401*7c478bd9Sstevel@tonic-gate 
402*7c478bd9Sstevel@tonic-gate 	for (; ; tq->iutq_next_timer_id++) {
403*7c478bd9Sstevel@tonic-gate 
404*7c478bd9Sstevel@tonic-gate 		if (tq->iutq_next_timer_id >= IU_TIMER_ID_MAX) {
405*7c478bd9Sstevel@tonic-gate 			if (have_wrapped)
406*7c478bd9Sstevel@tonic-gate 				return (-1);
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate 			have_wrapped = B_TRUE;
409*7c478bd9Sstevel@tonic-gate 			tq->iutq_next_timer_id = 0;
410*7c478bd9Sstevel@tonic-gate 		}
411*7c478bd9Sstevel@tonic-gate 
412*7c478bd9Sstevel@tonic-gate 		map_index = tq->iutq_next_timer_id / CHAR_BIT;
413*7c478bd9Sstevel@tonic-gate 		map_bit   = tq->iutq_next_timer_id % CHAR_BIT;
414*7c478bd9Sstevel@tonic-gate 
415*7c478bd9Sstevel@tonic-gate 		if ((tq->iutq_timer_id_map[map_index] & (1 << map_bit)) == 0)
416*7c478bd9Sstevel@tonic-gate 			break;
417*7c478bd9Sstevel@tonic-gate 	}
418*7c478bd9Sstevel@tonic-gate 
419*7c478bd9Sstevel@tonic-gate 	tq->iutq_timer_id_map[map_index] |= (1 << map_bit);
420*7c478bd9Sstevel@tonic-gate 	return (tq->iutq_next_timer_id++);
421*7c478bd9Sstevel@tonic-gate }
422*7c478bd9Sstevel@tonic-gate 
423*7c478bd9Sstevel@tonic-gate /*
424*7c478bd9Sstevel@tonic-gate  * release_timer_id(): releases a timer id back into the pool
425*7c478bd9Sstevel@tonic-gate  *
426*7c478bd9Sstevel@tonic-gate  *   input: iu_tq_t *: the timer queue
427*7c478bd9Sstevel@tonic-gate  *	    iu_timer_id_t: the timer id to release
428*7c478bd9Sstevel@tonic-gate  *  output: void
429*7c478bd9Sstevel@tonic-gate  */
430*7c478bd9Sstevel@tonic-gate 
431*7c478bd9Sstevel@tonic-gate static void
432*7c478bd9Sstevel@tonic-gate release_timer_id(iu_tq_t *tq, iu_timer_id_t timer_id)
433*7c478bd9Sstevel@tonic-gate {
434*7c478bd9Sstevel@tonic-gate 	unsigned int	map_index = timer_id / CHAR_BIT;
435*7c478bd9Sstevel@tonic-gate 	unsigned char	map_bit	  = timer_id % CHAR_BIT;
436*7c478bd9Sstevel@tonic-gate 
437*7c478bd9Sstevel@tonic-gate 	tq->iutq_timer_id_map[map_index] &= ~(1 << map_bit);
438*7c478bd9Sstevel@tonic-gate }
439