xref: /illumos-gate/usr/src/lib/libinetutil/common/tq.c (revision 19449258)
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