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 * 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 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 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 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 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 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 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 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 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 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 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 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 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