17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5dbed73cbSSangeeta Misra * Common Development and Distribution License (the "License").
6dbed73cbSSangeeta Misra * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
22dbed73cbSSangeeta Misra * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
237c478bd9Sstevel@tonic-gate * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate */
257c478bd9Sstevel@tonic-gate
267c478bd9Sstevel@tonic-gate #include <stdlib.h>
277c478bd9Sstevel@tonic-gate #include <limits.h>
287c478bd9Sstevel@tonic-gate #include <sys/time.h>
297c478bd9Sstevel@tonic-gate #include <sys/types.h>
307c478bd9Sstevel@tonic-gate #include <sys/sysmacros.h>
317c478bd9Sstevel@tonic-gate #include <sys/stropts.h> /* INFTIM */
327c478bd9Sstevel@tonic-gate
337c478bd9Sstevel@tonic-gate #include <libinetutil.h>
347c478bd9Sstevel@tonic-gate #include "libinetutil_impl.h"
357c478bd9Sstevel@tonic-gate
367c478bd9Sstevel@tonic-gate static iu_timer_node_t *pending_delete_chain = NULL;
377c478bd9Sstevel@tonic-gate
387c478bd9Sstevel@tonic-gate static void destroy_timer(iu_tq_t *, iu_timer_node_t *);
397c478bd9Sstevel@tonic-gate static iu_timer_id_t get_timer_id(iu_tq_t *);
407c478bd9Sstevel@tonic-gate static void release_timer_id(iu_tq_t *, iu_timer_id_t);
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate /*
437c478bd9Sstevel@tonic-gate * iu_tq_create(): creates, initializes and returns a timer queue for use
447c478bd9Sstevel@tonic-gate *
457c478bd9Sstevel@tonic-gate * input: void
467c478bd9Sstevel@tonic-gate * output: iu_tq_t *: the new timer queue
477c478bd9Sstevel@tonic-gate */
487c478bd9Sstevel@tonic-gate
497c478bd9Sstevel@tonic-gate iu_tq_t *
iu_tq_create(void)507c478bd9Sstevel@tonic-gate iu_tq_create(void)
517c478bd9Sstevel@tonic-gate {
527c478bd9Sstevel@tonic-gate return (calloc(1, sizeof (iu_tq_t)));
537c478bd9Sstevel@tonic-gate }
547c478bd9Sstevel@tonic-gate
557c478bd9Sstevel@tonic-gate /*
567c478bd9Sstevel@tonic-gate * iu_tq_destroy(): destroys an existing timer queue
577c478bd9Sstevel@tonic-gate *
587c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue to destroy
597c478bd9Sstevel@tonic-gate * output: void
607c478bd9Sstevel@tonic-gate */
617c478bd9Sstevel@tonic-gate
627c478bd9Sstevel@tonic-gate void
iu_tq_destroy(iu_tq_t * tq)637c478bd9Sstevel@tonic-gate iu_tq_destroy(iu_tq_t *tq)
647c478bd9Sstevel@tonic-gate {
657c478bd9Sstevel@tonic-gate iu_timer_node_t *node, *next_node;
667c478bd9Sstevel@tonic-gate
677c478bd9Sstevel@tonic-gate for (node = tq->iutq_head; node != NULL; node = next_node) {
687c478bd9Sstevel@tonic-gate next_node = node->iutn_next;
697c478bd9Sstevel@tonic-gate destroy_timer(tq, node);
707c478bd9Sstevel@tonic-gate }
717c478bd9Sstevel@tonic-gate
727c478bd9Sstevel@tonic-gate free(tq);
737c478bd9Sstevel@tonic-gate }
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate /*
767c478bd9Sstevel@tonic-gate * insert_timer(): inserts a timer node into a tq's timer list
777c478bd9Sstevel@tonic-gate *
787c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
797c478bd9Sstevel@tonic-gate * iu_timer_node_t *: the timer node to insert into the list
807c478bd9Sstevel@tonic-gate * uint64_t: the number of milliseconds before this timer fires
817c478bd9Sstevel@tonic-gate * output: void
827c478bd9Sstevel@tonic-gate */
837c478bd9Sstevel@tonic-gate
847c478bd9Sstevel@tonic-gate static void
insert_timer(iu_tq_t * tq,iu_timer_node_t * node,uint64_t msec)857c478bd9Sstevel@tonic-gate insert_timer(iu_tq_t *tq, iu_timer_node_t *node, uint64_t msec)
867c478bd9Sstevel@tonic-gate {
877c478bd9Sstevel@tonic-gate iu_timer_node_t *after = NULL;
887c478bd9Sstevel@tonic-gate
897c478bd9Sstevel@tonic-gate /*
907c478bd9Sstevel@tonic-gate * find the node to insert this new node "after". we do this
917c478bd9Sstevel@tonic-gate * instead of the more intuitive "insert before" because with
927c478bd9Sstevel@tonic-gate * the insert before approach, a null `before' node pointer
937c478bd9Sstevel@tonic-gate * is overloaded in meaning (it could be null because there
947c478bd9Sstevel@tonic-gate * are no items in the list, or it could be null because this
957c478bd9Sstevel@tonic-gate * is the last item on the list, which are very different cases).
967c478bd9Sstevel@tonic-gate */
977c478bd9Sstevel@tonic-gate
98*19449258SJosef 'Jeff' Sipek node->iutn_abs_timeout = gethrtime() + MSEC2NSEC(msec);
997c478bd9Sstevel@tonic-gate
1007c478bd9Sstevel@tonic-gate if (tq->iutq_head != NULL &&
1017c478bd9Sstevel@tonic-gate tq->iutq_head->iutn_abs_timeout < node->iutn_abs_timeout)
1027c478bd9Sstevel@tonic-gate for (after = tq->iutq_head; after->iutn_next != NULL;
1037c478bd9Sstevel@tonic-gate after = after->iutn_next)
1047c478bd9Sstevel@tonic-gate if (after->iutn_next->iutn_abs_timeout >
1057c478bd9Sstevel@tonic-gate node->iutn_abs_timeout)
1067c478bd9Sstevel@tonic-gate break;
1077c478bd9Sstevel@tonic-gate
1087c478bd9Sstevel@tonic-gate node->iutn_next = after ? after->iutn_next : tq->iutq_head;
1097c478bd9Sstevel@tonic-gate node->iutn_prev = after;
1107c478bd9Sstevel@tonic-gate if (after == NULL)
1117c478bd9Sstevel@tonic-gate tq->iutq_head = node;
1127c478bd9Sstevel@tonic-gate else
1137c478bd9Sstevel@tonic-gate after->iutn_next = node;
1147c478bd9Sstevel@tonic-gate
1157c478bd9Sstevel@tonic-gate if (node->iutn_next != NULL)
1167c478bd9Sstevel@tonic-gate node->iutn_next->iutn_prev = node;
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate
1197c478bd9Sstevel@tonic-gate /*
1207c478bd9Sstevel@tonic-gate * remove_timer(): removes a timer node from the tq's timer list
1217c478bd9Sstevel@tonic-gate *
1227c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
1237c478bd9Sstevel@tonic-gate * iu_timer_node_t *: the timer node to remove from the list
1247c478bd9Sstevel@tonic-gate * output: void
1257c478bd9Sstevel@tonic-gate */
1267c478bd9Sstevel@tonic-gate
1277c478bd9Sstevel@tonic-gate static void
remove_timer(iu_tq_t * tq,iu_timer_node_t * node)1287c478bd9Sstevel@tonic-gate remove_timer(iu_tq_t *tq, iu_timer_node_t *node)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate if (node->iutn_next != NULL)
1317c478bd9Sstevel@tonic-gate node->iutn_next->iutn_prev = node->iutn_prev;
1327c478bd9Sstevel@tonic-gate if (node->iutn_prev != NULL)
1337c478bd9Sstevel@tonic-gate node->iutn_prev->iutn_next = node->iutn_next;
1347c478bd9Sstevel@tonic-gate else
1357c478bd9Sstevel@tonic-gate tq->iutq_head = node->iutn_next;
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate
1387c478bd9Sstevel@tonic-gate /*
1397c478bd9Sstevel@tonic-gate * destroy_timer(): destroy a timer node
1407c478bd9Sstevel@tonic-gate *
1417c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue the timer node is associated with
1427c478bd9Sstevel@tonic-gate * iu_timer_node_t *: the node to free
1437c478bd9Sstevel@tonic-gate * output: void
1447c478bd9Sstevel@tonic-gate */
1457c478bd9Sstevel@tonic-gate
1467c478bd9Sstevel@tonic-gate static void
destroy_timer(iu_tq_t * tq,iu_timer_node_t * node)1477c478bd9Sstevel@tonic-gate destroy_timer(iu_tq_t *tq, iu_timer_node_t *node)
1487c478bd9Sstevel@tonic-gate {
1497c478bd9Sstevel@tonic-gate release_timer_id(tq, node->iutn_timer_id);
1507c478bd9Sstevel@tonic-gate
1517c478bd9Sstevel@tonic-gate /*
1527c478bd9Sstevel@tonic-gate * if we're in expire, don't delete the node yet, since it may
1537c478bd9Sstevel@tonic-gate * still be referencing it (through the expire_next pointers)
1547c478bd9Sstevel@tonic-gate */
1557c478bd9Sstevel@tonic-gate
1567c478bd9Sstevel@tonic-gate if (tq->iutq_in_expire) {
1577c478bd9Sstevel@tonic-gate node->iutn_pending_delete++;
1587c478bd9Sstevel@tonic-gate node->iutn_next = pending_delete_chain;
1597c478bd9Sstevel@tonic-gate pending_delete_chain = node;
1607c478bd9Sstevel@tonic-gate } else
1617c478bd9Sstevel@tonic-gate free(node);
1627c478bd9Sstevel@tonic-gate
1637c478bd9Sstevel@tonic-gate }
1647c478bd9Sstevel@tonic-gate
1657c478bd9Sstevel@tonic-gate /*
1667c478bd9Sstevel@tonic-gate * iu_schedule_timer(): creates and inserts a timer in the tq's timer list
1677c478bd9Sstevel@tonic-gate *
1687c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
1697c478bd9Sstevel@tonic-gate * uint32_t: the number of seconds before this timer fires
1707c478bd9Sstevel@tonic-gate * iu_tq_callback_t *: the function to call when the timer fires
1717c478bd9Sstevel@tonic-gate * void *: an argument to pass to the called back function
1727c478bd9Sstevel@tonic-gate * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
1737c478bd9Sstevel@tonic-gate */
1747c478bd9Sstevel@tonic-gate
1757c478bd9Sstevel@tonic-gate iu_timer_id_t
iu_schedule_timer(iu_tq_t * tq,uint32_t sec,iu_tq_callback_t * callback,void * arg)1767c478bd9Sstevel@tonic-gate iu_schedule_timer(iu_tq_t *tq, uint32_t sec, iu_tq_callback_t *callback,
1777c478bd9Sstevel@tonic-gate void *arg)
1787c478bd9Sstevel@tonic-gate {
1797c478bd9Sstevel@tonic-gate return (iu_schedule_timer_ms(tq, sec * MILLISEC, callback, arg));
1807c478bd9Sstevel@tonic-gate }
1817c478bd9Sstevel@tonic-gate
1827c478bd9Sstevel@tonic-gate /*
1837c478bd9Sstevel@tonic-gate * iu_schedule_ms_timer(): creates and inserts a timer in the tq's timer list,
1847c478bd9Sstevel@tonic-gate * using millisecond granularity
1857c478bd9Sstevel@tonic-gate *
1867c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
1877c478bd9Sstevel@tonic-gate * uint64_t: the number of milliseconds before this timer fires
1887c478bd9Sstevel@tonic-gate * iu_tq_callback_t *: the function to call when the timer fires
1897c478bd9Sstevel@tonic-gate * void *: an argument to pass to the called back function
1907c478bd9Sstevel@tonic-gate * output: iu_timer_id_t: the new timer's timer id on success, -1 on failure
1917c478bd9Sstevel@tonic-gate */
1927c478bd9Sstevel@tonic-gate iu_timer_id_t
iu_schedule_timer_ms(iu_tq_t * tq,uint64_t ms,iu_tq_callback_t * callback,void * arg)1937c478bd9Sstevel@tonic-gate iu_schedule_timer_ms(iu_tq_t *tq, uint64_t ms, iu_tq_callback_t *callback,
1947c478bd9Sstevel@tonic-gate void *arg)
1957c478bd9Sstevel@tonic-gate {
1967c478bd9Sstevel@tonic-gate iu_timer_node_t *node = calloc(1, sizeof (iu_timer_node_t));
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate if (node == NULL)
1997c478bd9Sstevel@tonic-gate return (-1);
2007c478bd9Sstevel@tonic-gate
2017c478bd9Sstevel@tonic-gate node->iutn_callback = callback;
2027c478bd9Sstevel@tonic-gate node->iutn_arg = arg;
2037c478bd9Sstevel@tonic-gate node->iutn_timer_id = get_timer_id(tq);
2047c478bd9Sstevel@tonic-gate if (node->iutn_timer_id == -1) {
2057c478bd9Sstevel@tonic-gate free(node);
2067c478bd9Sstevel@tonic-gate return (-1);
2077c478bd9Sstevel@tonic-gate }
2087c478bd9Sstevel@tonic-gate
2097c478bd9Sstevel@tonic-gate insert_timer(tq, node, ms);
2107c478bd9Sstevel@tonic-gate
2117c478bd9Sstevel@tonic-gate return (node->iutn_timer_id);
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate
2147c478bd9Sstevel@tonic-gate /*
2157c478bd9Sstevel@tonic-gate * iu_cancel_timer(): cancels a pending timer from a timer queue's timer list
2167c478bd9Sstevel@tonic-gate *
2177c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
2187c478bd9Sstevel@tonic-gate * iu_timer_id_t: the timer id returned from iu_schedule_timer
2197c478bd9Sstevel@tonic-gate * void **: if non-NULL, a place to return the argument passed to
2207c478bd9Sstevel@tonic-gate * iu_schedule_timer
2217c478bd9Sstevel@tonic-gate * output: int: 1 on success, 0 on failure
2227c478bd9Sstevel@tonic-gate */
2237c478bd9Sstevel@tonic-gate
2247c478bd9Sstevel@tonic-gate int
iu_cancel_timer(iu_tq_t * tq,iu_timer_id_t timer_id,void ** arg)2257c478bd9Sstevel@tonic-gate iu_cancel_timer(iu_tq_t *tq, iu_timer_id_t timer_id, void **arg)
2267c478bd9Sstevel@tonic-gate {
2277c478bd9Sstevel@tonic-gate iu_timer_node_t *node;
2287c478bd9Sstevel@tonic-gate
2297c478bd9Sstevel@tonic-gate if (timer_id == -1)
2307c478bd9Sstevel@tonic-gate return (0);
2317c478bd9Sstevel@tonic-gate
2327c478bd9Sstevel@tonic-gate for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
2337c478bd9Sstevel@tonic-gate if (node->iutn_timer_id == timer_id) {
2347c478bd9Sstevel@tonic-gate if (arg != NULL)
2357c478bd9Sstevel@tonic-gate *arg = node->iutn_arg;
2367c478bd9Sstevel@tonic-gate remove_timer(tq, node);
2377c478bd9Sstevel@tonic-gate destroy_timer(tq, node);
2387c478bd9Sstevel@tonic-gate return (1);
2397c478bd9Sstevel@tonic-gate }
2407c478bd9Sstevel@tonic-gate }
2417c478bd9Sstevel@tonic-gate return (0);
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate /*
2457c478bd9Sstevel@tonic-gate * iu_adjust_timer(): adjusts the fire time of a timer in the tq's timer list
2467c478bd9Sstevel@tonic-gate *
2477c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
2487c478bd9Sstevel@tonic-gate * iu_timer_id_t: the timer id returned from iu_schedule_timer
2497c478bd9Sstevel@tonic-gate * uint32_t: the number of seconds before this timer fires
2507c478bd9Sstevel@tonic-gate * output: int: 1 on success, 0 on failure
2517c478bd9Sstevel@tonic-gate */
2527c478bd9Sstevel@tonic-gate
2537c478bd9Sstevel@tonic-gate int
iu_adjust_timer(iu_tq_t * tq,iu_timer_id_t timer_id,uint32_t sec)2547c478bd9Sstevel@tonic-gate iu_adjust_timer(iu_tq_t *tq, iu_timer_id_t timer_id, uint32_t sec)
2557c478bd9Sstevel@tonic-gate {
2567c478bd9Sstevel@tonic-gate iu_timer_node_t *node;
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate if (timer_id == -1)
2597c478bd9Sstevel@tonic-gate return (0);
2607c478bd9Sstevel@tonic-gate
2617c478bd9Sstevel@tonic-gate for (node = tq->iutq_head; node != NULL; node = node->iutn_next) {
2627c478bd9Sstevel@tonic-gate if (node->iutn_timer_id == timer_id) {
2637c478bd9Sstevel@tonic-gate remove_timer(tq, node);
2647c478bd9Sstevel@tonic-gate insert_timer(tq, node, sec * MILLISEC);
2657c478bd9Sstevel@tonic-gate return (1);
2667c478bd9Sstevel@tonic-gate }
2677c478bd9Sstevel@tonic-gate }
2687c478bd9Sstevel@tonic-gate return (0);
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate
2717c478bd9Sstevel@tonic-gate /*
2727c478bd9Sstevel@tonic-gate * iu_earliest_timer(): returns the time until the next timer fires on a tq
2737c478bd9Sstevel@tonic-gate *
2747c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
2757c478bd9Sstevel@tonic-gate * output: int: the number of milliseconds until the next timer (up to
2767c478bd9Sstevel@tonic-gate * a maximum value of INT_MAX), or INFTIM if no timers are pending.
2777c478bd9Sstevel@tonic-gate */
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate int
iu_earliest_timer(iu_tq_t * tq)2807c478bd9Sstevel@tonic-gate iu_earliest_timer(iu_tq_t *tq)
2817c478bd9Sstevel@tonic-gate {
2827c478bd9Sstevel@tonic-gate unsigned long long timeout_interval;
2837c478bd9Sstevel@tonic-gate hrtime_t current_time = gethrtime();
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate if (tq->iutq_head == NULL)
2867c478bd9Sstevel@tonic-gate return (INFTIM);
2877c478bd9Sstevel@tonic-gate
2887c478bd9Sstevel@tonic-gate /*
2897c478bd9Sstevel@tonic-gate * event might've already happened if we haven't gotten a chance to
2907c478bd9Sstevel@tonic-gate * run in a while; return zero and pretend it just expired.
2917c478bd9Sstevel@tonic-gate */
2927c478bd9Sstevel@tonic-gate
2937c478bd9Sstevel@tonic-gate if (tq->iutq_head->iutn_abs_timeout <= current_time)
2947c478bd9Sstevel@tonic-gate return (0);
2957c478bd9Sstevel@tonic-gate
2967c478bd9Sstevel@tonic-gate /*
2977c478bd9Sstevel@tonic-gate * since the timers are ordered in absolute time-to-fire, just
2987c478bd9Sstevel@tonic-gate * subtract from the head of the list.
2997c478bd9Sstevel@tonic-gate */
3007c478bd9Sstevel@tonic-gate
3017c478bd9Sstevel@tonic-gate timeout_interval =
3027c478bd9Sstevel@tonic-gate (tq->iutq_head->iutn_abs_timeout - current_time) / 1000000;
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate return (MIN(timeout_interval, INT_MAX));
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate
3077c478bd9Sstevel@tonic-gate /*
3087c478bd9Sstevel@tonic-gate * iu_expire_timers(): expires all pending timers on a given timer queue
3097c478bd9Sstevel@tonic-gate *
3107c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
3117c478bd9Sstevel@tonic-gate * output: int: the number of timers expired
3127c478bd9Sstevel@tonic-gate */
3137c478bd9Sstevel@tonic-gate
3147c478bd9Sstevel@tonic-gate int
iu_expire_timers(iu_tq_t * tq)3157c478bd9Sstevel@tonic-gate iu_expire_timers(iu_tq_t *tq)
3167c478bd9Sstevel@tonic-gate {
3177c478bd9Sstevel@tonic-gate iu_timer_node_t *node, *next_node;
3187c478bd9Sstevel@tonic-gate int n_expired = 0;
3197c478bd9Sstevel@tonic-gate hrtime_t current_time = gethrtime();
3207c478bd9Sstevel@tonic-gate
3217c478bd9Sstevel@tonic-gate /*
3227c478bd9Sstevel@tonic-gate * in_expire is in the iu_tq_t instead of being passed through as
3237c478bd9Sstevel@tonic-gate * an argument to remove_timer() below since the callback
3247c478bd9Sstevel@tonic-gate * function may call iu_cancel_timer() itself as well.
3257c478bd9Sstevel@tonic-gate */
3267c478bd9Sstevel@tonic-gate
3277c478bd9Sstevel@tonic-gate tq->iutq_in_expire++;
3287c478bd9Sstevel@tonic-gate
3297c478bd9Sstevel@tonic-gate /*
3307c478bd9Sstevel@tonic-gate * this function builds another linked list of timer nodes
3317c478bd9Sstevel@tonic-gate * through `expire_next' because the normal linked list
3327c478bd9Sstevel@tonic-gate * may be changed as a result of callbacks canceling and
3337c478bd9Sstevel@tonic-gate * scheduling timeouts, and thus can't be trusted.
3347c478bd9Sstevel@tonic-gate */
3357c478bd9Sstevel@tonic-gate
3367c478bd9Sstevel@tonic-gate for (node = tq->iutq_head; node != NULL; node = node->iutn_next)
3377c478bd9Sstevel@tonic-gate node->iutn_expire_next = node->iutn_next;
3387c478bd9Sstevel@tonic-gate
3397c478bd9Sstevel@tonic-gate for (node = tq->iutq_head; node != NULL;
3407c478bd9Sstevel@tonic-gate node = node->iutn_expire_next) {
3417c478bd9Sstevel@tonic-gate
342dbed73cbSSangeeta Misra /*
343dbed73cbSSangeeta Misra * If the timeout is within 1 millisec of current time,
344dbed73cbSSangeeta Misra * consider it as expired already. We do this because
345dbed73cbSSangeeta Misra * iu_earliest_timer() only has millisec granularity.
346dbed73cbSSangeeta Misra * So we should also use millisec grandularity in
347dbed73cbSSangeeta Misra * comparing timeout values.
348dbed73cbSSangeeta Misra */
349dbed73cbSSangeeta Misra if (node->iutn_abs_timeout - current_time > 1000000)
3507c478bd9Sstevel@tonic-gate break;
3517c478bd9Sstevel@tonic-gate
3527c478bd9Sstevel@tonic-gate /*
3537c478bd9Sstevel@tonic-gate * fringe condition: two timers fire at the "same
3547c478bd9Sstevel@tonic-gate * time" (i.e., they're both scheduled called back in
3557c478bd9Sstevel@tonic-gate * this loop) and one cancels the other. in this
3567c478bd9Sstevel@tonic-gate * case, the timer which has already been "cancelled"
3577c478bd9Sstevel@tonic-gate * should not be called back.
3587c478bd9Sstevel@tonic-gate */
3597c478bd9Sstevel@tonic-gate
3607c478bd9Sstevel@tonic-gate if (node->iutn_pending_delete)
3617c478bd9Sstevel@tonic-gate continue;
3627c478bd9Sstevel@tonic-gate
3637c478bd9Sstevel@tonic-gate /*
3647c478bd9Sstevel@tonic-gate * we remove the timer before calling back the callback
3657c478bd9Sstevel@tonic-gate * so that a callback which accidentally tries to cancel
3667c478bd9Sstevel@tonic-gate * itself (through whatever means) doesn't succeed.
3677c478bd9Sstevel@tonic-gate */
3687c478bd9Sstevel@tonic-gate
3697c478bd9Sstevel@tonic-gate n_expired++;
3707c478bd9Sstevel@tonic-gate remove_timer(tq, node);
3717c478bd9Sstevel@tonic-gate destroy_timer(tq, node);
3727c478bd9Sstevel@tonic-gate node->iutn_callback(tq, node->iutn_arg);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate
3757c478bd9Sstevel@tonic-gate tq->iutq_in_expire--;
3767c478bd9Sstevel@tonic-gate
3777c478bd9Sstevel@tonic-gate /*
3787c478bd9Sstevel@tonic-gate * any cancels that took place whilst we were expiring timeouts
3797c478bd9Sstevel@tonic-gate * ended up on the `pending_delete_chain'. delete them now
3807c478bd9Sstevel@tonic-gate * that it's safe.
3817c478bd9Sstevel@tonic-gate */
3827c478bd9Sstevel@tonic-gate
3837c478bd9Sstevel@tonic-gate for (node = pending_delete_chain; node != NULL; node = next_node) {
3847c478bd9Sstevel@tonic-gate next_node = node->iutn_next;
3857c478bd9Sstevel@tonic-gate free(node);
3867c478bd9Sstevel@tonic-gate }
3877c478bd9Sstevel@tonic-gate pending_delete_chain = NULL;
3887c478bd9Sstevel@tonic-gate
3897c478bd9Sstevel@tonic-gate return (n_expired);
3907c478bd9Sstevel@tonic-gate }
3917c478bd9Sstevel@tonic-gate
3927c478bd9Sstevel@tonic-gate /*
3937c478bd9Sstevel@tonic-gate * get_timer_id(): allocates a timer id from the pool
3947c478bd9Sstevel@tonic-gate *
3957c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
3967c478bd9Sstevel@tonic-gate * output: iu_timer_id_t: the allocated timer id, or -1 if none available
3977c478bd9Sstevel@tonic-gate */
3987c478bd9Sstevel@tonic-gate
3997c478bd9Sstevel@tonic-gate static iu_timer_id_t
get_timer_id(iu_tq_t * tq)4007c478bd9Sstevel@tonic-gate get_timer_id(iu_tq_t *tq)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate unsigned int map_index;
4037c478bd9Sstevel@tonic-gate unsigned char map_bit;
4047c478bd9Sstevel@tonic-gate boolean_t have_wrapped = B_FALSE;
4057c478bd9Sstevel@tonic-gate
4067c478bd9Sstevel@tonic-gate for (; ; tq->iutq_next_timer_id++) {
4077c478bd9Sstevel@tonic-gate
4087c478bd9Sstevel@tonic-gate if (tq->iutq_next_timer_id >= IU_TIMER_ID_MAX) {
4097c478bd9Sstevel@tonic-gate if (have_wrapped)
4107c478bd9Sstevel@tonic-gate return (-1);
4117c478bd9Sstevel@tonic-gate
4127c478bd9Sstevel@tonic-gate have_wrapped = B_TRUE;
4137c478bd9Sstevel@tonic-gate tq->iutq_next_timer_id = 0;
4147c478bd9Sstevel@tonic-gate }
4157c478bd9Sstevel@tonic-gate
4167c478bd9Sstevel@tonic-gate map_index = tq->iutq_next_timer_id / CHAR_BIT;
4177c478bd9Sstevel@tonic-gate map_bit = tq->iutq_next_timer_id % CHAR_BIT;
4187c478bd9Sstevel@tonic-gate
4197c478bd9Sstevel@tonic-gate if ((tq->iutq_timer_id_map[map_index] & (1 << map_bit)) == 0)
4207c478bd9Sstevel@tonic-gate break;
4217c478bd9Sstevel@tonic-gate }
4227c478bd9Sstevel@tonic-gate
4237c478bd9Sstevel@tonic-gate tq->iutq_timer_id_map[map_index] |= (1 << map_bit);
4247c478bd9Sstevel@tonic-gate return (tq->iutq_next_timer_id++);
4257c478bd9Sstevel@tonic-gate }
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate /*
4287c478bd9Sstevel@tonic-gate * release_timer_id(): releases a timer id back into the pool
4297c478bd9Sstevel@tonic-gate *
4307c478bd9Sstevel@tonic-gate * input: iu_tq_t *: the timer queue
4317c478bd9Sstevel@tonic-gate * iu_timer_id_t: the timer id to release
4327c478bd9Sstevel@tonic-gate * output: void
4337c478bd9Sstevel@tonic-gate */
4347c478bd9Sstevel@tonic-gate
4357c478bd9Sstevel@tonic-gate static void
release_timer_id(iu_tq_t * tq,iu_timer_id_t timer_id)4367c478bd9Sstevel@tonic-gate release_timer_id(iu_tq_t *tq, iu_timer_id_t timer_id)
4377c478bd9Sstevel@tonic-gate {
4387c478bd9Sstevel@tonic-gate unsigned int map_index = timer_id / CHAR_BIT;
4397c478bd9Sstevel@tonic-gate unsigned char map_bit = timer_id % CHAR_BIT;
4407c478bd9Sstevel@tonic-gate
4417c478bd9Sstevel@tonic-gate tq->iutq_timer_id_map[map_index] &= ~(1 << map_bit);
4427c478bd9Sstevel@tonic-gate }
443