4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <sys/stropts.h> /* INFTIM */
33 #include <libinetutil.h>
34 #include "libinetutil_impl.h"
36 static iu_timer_node_t
*pending_delete_chain
= NULL
;
38 static void destroy_timer(iu_tq_t
*, iu_timer_node_t
*);
39 static iu_timer_id_t
get_timer_id(iu_tq_t
*);
40 static void release_timer_id(iu_tq_t
*, iu_timer_id_t
);
43 * iu_tq_create(): creates, initializes and returns a timer queue for use
46 * output: iu_tq_t *: the new timer queue
52 return (calloc(1, sizeof (iu_tq_t
)));
56 * iu_tq_destroy(): destroys an existing timer queue
58 * input: iu_tq_t *: the timer queue to destroy
63 iu_tq_destroy(iu_tq_t
*tq
)
65 iu_timer_node_t
*node
, *next_node
;
67 for (node
= tq
->iutq_head
; node
!= NULL
; node
= next_node
) {
68 next_node
= node
->iutn_next
;
69 destroy_timer(tq
, node
);
76 * insert_timer(): inserts a timer node into a tq's timer list
78 * input: iu_tq_t *: the timer queue
79 * iu_timer_node_t *: the timer node to insert into the list
80 * uint64_t: the number of milliseconds before this timer fires
85 insert_timer(iu_tq_t
*tq
, iu_timer_node_t
*node
, uint64_t msec
)
87 iu_timer_node_t
*after
= NULL
;
90 * find the node to insert this new node "after". we do this
91 * instead of the more intuitive "insert before" because with
92 * the insert before approach, a null `before' node pointer
93 * is overloaded in meaning (it could be null because there
94 * are no items in the list, or it could be null because this
95 * is the last item on the list, which are very different cases).
98 node
->iutn_abs_timeout
= gethrtime() + MSEC2NSEC(msec
);
100 if (tq
->iutq_head
!= NULL
&&
101 tq
->iutq_head
->iutn_abs_timeout
< node
->iutn_abs_timeout
)
102 for (after
= tq
->iutq_head
; after
->iutn_next
!= NULL
;
103 after
= after
->iutn_next
)
104 if (after
->iutn_next
->iutn_abs_timeout
>
105 node
->iutn_abs_timeout
)
108 node
->iutn_next
= after
? after
->iutn_next
: tq
->iutq_head
;
109 node
->iutn_prev
= after
;
111 tq
->iutq_head
= node
;
113 after
->iutn_next
= node
;
115 if (node
->iutn_next
!= NULL
)
116 node
->iutn_next
->iutn_prev
= node
;
120 * remove_timer(): removes a timer node from the tq's timer list
122 * input: iu_tq_t *: the timer queue
123 * iu_timer_node_t *: the timer node to remove from the list
128 remove_timer(iu_tq_t
*tq
, iu_timer_node_t
*node
)
130 if (node
->iutn_next
!= NULL
)
131 node
->iutn_next
->iutn_prev
= node
->iutn_prev
;
132 if (node
->iutn_prev
!= NULL
)
133 node
->iutn_prev
->iutn_next
= node
->iutn_next
;
135 tq
->iutq_head
= node
->iutn_next
;
139 * destroy_timer(): destroy a timer node
141 * input: iu_tq_t *: the timer queue the timer node is associated with
142 * iu_timer_node_t *: the node to free
147 destroy_timer(iu_tq_t
*tq
, iu_timer_node_t
*node
)
149 release_timer_id(tq
, node
->iutn_timer_id
);
152 * if we're in expire, don't delete the node yet, since it may
153 * still be referencing it (through the expire_next pointers)
156 if (tq
->iutq_in_expire
) {
157 node
->iutn_pending_delete
++;
158 node
->iutn_next
= pending_delete_chain
;
159 pending_delete_chain
= node
;
166 * iu_schedule_timer(): creates and inserts a timer in the tq's timer list
168 * input: iu_tq_t *: the timer queue
169 * uint32_t: the number of seconds before this timer fires
170 * iu_tq_callback_t *: the function to call when the timer fires
171 * void *: an argument to pass to the called back function
172 * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
176 iu_schedule_timer(iu_tq_t
*tq
, uint32_t sec
, iu_tq_callback_t
*callback
,
179 return (iu_schedule_timer_ms(tq
, sec
* MILLISEC
, callback
, arg
));
183 * iu_schedule_ms_timer(): creates and inserts a timer in the tq's timer list,
184 * using millisecond granularity
186 * input: iu_tq_t *: the timer queue
187 * uint64_t: the number of milliseconds before this timer fires
188 * iu_tq_callback_t *: the function to call when the timer fires
189 * void *: an argument to pass to the called back function
190 * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
193 iu_schedule_timer_ms(iu_tq_t
*tq
, uint64_t ms
, iu_tq_callback_t
*callback
,
196 iu_timer_node_t
*node
= calloc(1, sizeof (iu_timer_node_t
));
201 node
->iutn_callback
= callback
;
202 node
->iutn_arg
= arg
;
203 node
->iutn_timer_id
= get_timer_id(tq
);
204 if (node
->iutn_timer_id
== -1) {
209 insert_timer(tq
, node
, ms
);
211 return (node
->iutn_timer_id
);
215 * iu_cancel_timer(): cancels a pending timer from a timer queue's timer list
217 * input: iu_tq_t *: the timer queue
218 * iu_timer_id_t: the timer id returned from iu_schedule_timer
219 * void **: if non-NULL, a place to return the argument passed to
221 * output: int: 1 on success, 0 on failure
225 iu_cancel_timer(iu_tq_t
*tq
, iu_timer_id_t timer_id
, void **arg
)
227 iu_timer_node_t
*node
;
232 for (node
= tq
->iutq_head
; node
!= NULL
; node
= node
->iutn_next
) {
233 if (node
->iutn_timer_id
== timer_id
) {
235 *arg
= node
->iutn_arg
;
236 remove_timer(tq
, node
);
237 destroy_timer(tq
, node
);
245 * iu_adjust_timer(): adjusts the fire time of a timer in the tq's timer list
247 * input: iu_tq_t *: the timer queue
248 * iu_timer_id_t: the timer id returned from iu_schedule_timer
249 * uint32_t: the number of seconds before this timer fires
250 * output: int: 1 on success, 0 on failure
254 iu_adjust_timer(iu_tq_t
*tq
, iu_timer_id_t timer_id
, uint32_t sec
)
256 iu_timer_node_t
*node
;
261 for (node
= tq
->iutq_head
; node
!= NULL
; node
= node
->iutn_next
) {
262 if (node
->iutn_timer_id
== timer_id
) {
263 remove_timer(tq
, node
);
264 insert_timer(tq
, node
, sec
* MILLISEC
);
272 * iu_earliest_timer(): returns the time until the next timer fires on a tq
274 * input: iu_tq_t *: the timer queue
275 * output: int: the number of milliseconds until the next timer (up to
276 * a maximum value of INT_MAX), or INFTIM if no timers are pending.
280 iu_earliest_timer(iu_tq_t
*tq
)
282 unsigned long long timeout_interval
;
283 hrtime_t current_time
= gethrtime();
285 if (tq
->iutq_head
== NULL
)
289 * event might've already happened if we haven't gotten a chance to
290 * run in a while; return zero and pretend it just expired.
293 if (tq
->iutq_head
->iutn_abs_timeout
<= current_time
)
297 * since the timers are ordered in absolute time-to-fire, just
298 * subtract from the head of the list.
302 (tq
->iutq_head
->iutn_abs_timeout
- current_time
) / 1000000;
304 return (MIN(timeout_interval
, INT_MAX
));
308 * iu_expire_timers(): expires all pending timers on a given timer queue
310 * input: iu_tq_t *: the timer queue
311 * output: int: the number of timers expired
315 iu_expire_timers(iu_tq_t
*tq
)
317 iu_timer_node_t
*node
, *next_node
;
319 hrtime_t current_time
= gethrtime();
322 * in_expire is in the iu_tq_t instead of being passed through as
323 * an argument to remove_timer() below since the callback
324 * function may call iu_cancel_timer() itself as well.
327 tq
->iutq_in_expire
++;
330 * this function builds another linked list of timer nodes
331 * through `expire_next' because the normal linked list
332 * may be changed as a result of callbacks canceling and
333 * scheduling timeouts, and thus can't be trusted.
336 for (node
= tq
->iutq_head
; node
!= NULL
; node
= node
->iutn_next
)
337 node
->iutn_expire_next
= node
->iutn_next
;
339 for (node
= tq
->iutq_head
; node
!= NULL
;
340 node
= node
->iutn_expire_next
) {
343 * If the timeout is within 1 millisec of current time,
344 * consider it as expired already. We do this because
345 * iu_earliest_timer() only has millisec granularity.
346 * So we should also use millisec grandularity in
347 * comparing timeout values.
349 if (node
->iutn_abs_timeout
- current_time
> 1000000)
353 * fringe condition: two timers fire at the "same
354 * time" (i.e., they're both scheduled called back in
355 * this loop) and one cancels the other. in this
356 * case, the timer which has already been "cancelled"
357 * should not be called back.
360 if (node
->iutn_pending_delete
)
364 * we remove the timer before calling back the callback
365 * so that a callback which accidentally tries to cancel
366 * itself (through whatever means) doesn't succeed.
370 remove_timer(tq
, node
);
371 destroy_timer(tq
, node
);
372 node
->iutn_callback(tq
, node
->iutn_arg
);
375 tq
->iutq_in_expire
--;
378 * any cancels that took place whilst we were expiring timeouts
379 * ended up on the `pending_delete_chain'. delete them now
383 for (node
= pending_delete_chain
; node
!= NULL
; node
= next_node
) {
384 next_node
= node
->iutn_next
;
387 pending_delete_chain
= NULL
;
393 * get_timer_id(): allocates a timer id from the pool
395 * input: iu_tq_t *: the timer queue
396 * output: iu_timer_id_t: the allocated timer id, or -1 if none available
400 get_timer_id(iu_tq_t
*tq
)
402 unsigned int map_index
;
403 unsigned char map_bit
;
404 boolean_t have_wrapped
= B_FALSE
;
406 for (; ; tq
->iutq_next_timer_id
++) {
408 if (tq
->iutq_next_timer_id
>= IU_TIMER_ID_MAX
) {
412 have_wrapped
= B_TRUE
;
413 tq
->iutq_next_timer_id
= 0;
416 map_index
= tq
->iutq_next_timer_id
/ CHAR_BIT
;
417 map_bit
= tq
->iutq_next_timer_id
% CHAR_BIT
;
419 if ((tq
->iutq_timer_id_map
[map_index
] & (1 << map_bit
)) == 0)
423 tq
->iutq_timer_id_map
[map_index
] |= (1 << map_bit
);
424 return (tq
->iutq_next_timer_id
++);
428 * release_timer_id(): releases a timer id back into the pool
430 * input: iu_tq_t *: the timer queue
431 * iu_timer_id_t: the timer id to release
436 release_timer_id(iu_tq_t
*tq
, iu_timer_id_t timer_id
)
438 unsigned int map_index
= timer_id
/ CHAR_BIT
;
439 unsigned char map_bit
= timer_id
% CHAR_BIT
;
441 tq
->iutq_timer_id_map
[map_index
] &= ~(1 << map_bit
);