xref: /onnv-gate/usr/src/lib/libsip/common/sip_timeout.c (revision 3439:0302bfe973fe)
12882Svi117747 /*
22882Svi117747  * CDDL HEADER START
32882Svi117747  *
42882Svi117747  * The contents of this file are subject to the terms of the
52882Svi117747  * Common Development and Distribution License (the "License").
62882Svi117747  * You may not use this file except in compliance with the License.
72882Svi117747  *
82882Svi117747  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92882Svi117747  * or http://www.opensolaris.org/os/licensing.
102882Svi117747  * See the License for the specific language governing permissions
112882Svi117747  * and limitations under the License.
122882Svi117747  *
132882Svi117747  * When distributing Covered Code, include this CDDL HEADER in each
142882Svi117747  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152882Svi117747  * If applicable, add the following below this CDDL HEADER, with the
162882Svi117747  * fields enclosed by brackets "[]" replaced with your own identifying
172882Svi117747  * information: Portions Copyright [yyyy] [name of copyright owner]
182882Svi117747  *
192882Svi117747  * CDDL HEADER END
202882Svi117747  */
212882Svi117747 
222882Svi117747 /*
23*3439Svi117747  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
242882Svi117747  * Use is subject to license terms.
252882Svi117747  */
262882Svi117747 
272882Svi117747 #pragma ident	"%Z%%M%	%I%	%E% SMI"
282882Svi117747 
292882Svi117747 /*
302882Svi117747  * Simple implementation of timeout functionality. The granuality is a sec
312882Svi117747  */
322882Svi117747 #include <pthread.h>
332882Svi117747 #include <stdlib.h>
342882Svi117747 
352882Svi117747 uint_t		sip_timeout(void *arg, void (*callback_func)(void *),
362882Svi117747 		    struct timeval *timeout_time);
372882Svi117747 boolean_t	sip_untimeout(uint_t);
382882Svi117747 
392882Svi117747 typedef struct timeout {
402882Svi117747 	struct timeout *sip_timeout_next;
412882Svi117747 	hrtime_t sip_timeout_val;
422882Svi117747 	void (*sip_timeout_callback_func)(void *);
432882Svi117747 	void *sip_timeout_callback_func_arg;
442882Svi117747 	int   sip_timeout_id;
452882Svi117747 } sip_timeout_t;
462882Svi117747 
472882Svi117747 static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
482882Svi117747 static pthread_cond_t  timeout_cond_var = PTHREAD_COND_INITIALIZER;
492882Svi117747 static sip_timeout_t *timeout_list;
502882Svi117747 static sip_timeout_t *timeout_current_start;
512882Svi117747 static sip_timeout_t *timeout_current_end;
522882Svi117747 
532882Svi117747 /*
542882Svi117747  * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
552882Svi117747  */
562882Svi117747 #define	LONG_SLEEP_TIME	(0x15180LL * 0x3B9ACA00LL)
572882Svi117747 
582882Svi117747 uint_t timer_id = 0;
592882Svi117747 
602882Svi117747 /*
612882Svi117747  * Invoke the callback function
622882Svi117747  */
632882Svi117747 /* ARGSUSED */
642882Svi117747 static void *
sip_run_to_functions(void * arg)652882Svi117747 sip_run_to_functions(void *arg)
662882Svi117747 {
672882Svi117747 	sip_timeout_t *timeout = NULL;
682882Svi117747 
692882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
702882Svi117747 	while (timeout_current_start != NULL) {
712882Svi117747 		timeout = timeout_current_start;
722882Svi117747 		if (timeout_current_end == timeout_current_start)
732882Svi117747 			timeout_current_start = timeout_current_end = NULL;
742882Svi117747 		else
752882Svi117747 			timeout_current_start = timeout->sip_timeout_next;
762882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
772882Svi117747 		timeout->sip_timeout_callback_func(
782882Svi117747 		    timeout->sip_timeout_callback_func_arg);
792882Svi117747 		free(timeout);
802882Svi117747 		(void) pthread_mutex_lock(&timeout_mutex);
812882Svi117747 	}
822882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
832882Svi117747 	pthread_exit(NULL);
842882Svi117747 	return ((void *)0);
852882Svi117747 }
862882Svi117747 
872882Svi117747 /*
882882Svi117747  * In the very very unlikely case timer id wraps around and we have two timers
892882Svi117747  * with the same id. If that happens timer with the least amount of time left
902882Svi117747  * will be deleted. In case both timers have same time left than the one that
912882Svi117747  * was scheduled first will be deleted as it will be in the front of the list.
922882Svi117747  */
932882Svi117747 boolean_t
sip_untimeout(uint_t id)942882Svi117747 sip_untimeout(uint_t id)
952882Svi117747 {
962882Svi117747 	boolean_t	ret = B_FALSE;
972882Svi117747 	sip_timeout_t	*current, *last;
982882Svi117747 
992882Svi117747 	last = NULL;
1002882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
1012882Svi117747 
1022882Svi117747 	/*
1032882Svi117747 	 * Check if this is in the to-be run list
1042882Svi117747 	 */
1052882Svi117747 	if (timeout_current_start != NULL) {
1062882Svi117747 		current = timeout_current_start;
1072882Svi117747 		while (current != NULL) {
1082882Svi117747 			if (current->sip_timeout_id == id) {
1092882Svi117747 				if (current == timeout_current_start) {
1102882Svi117747 					timeout_current_start =
1112882Svi117747 					    current->sip_timeout_next;
1122882Svi117747 				} else {
1132882Svi117747 					last->sip_timeout_next =
1142882Svi117747 					    current->sip_timeout_next;
1152882Svi117747 				}
1162882Svi117747 				if (current == timeout_current_end)
1172882Svi117747 					timeout_current_end = last;
1182882Svi117747 				if (current->sip_timeout_callback_func_arg !=
1192882Svi117747 				    NULL) {
1202882Svi117747 					free(current->
1212882Svi117747 					    sip_timeout_callback_func_arg);
1222882Svi117747 					current->sip_timeout_callback_func_arg =
1232882Svi117747 					    NULL;
1242882Svi117747 				}
1252882Svi117747 				free(current);
1262882Svi117747 				ret = B_TRUE;
1272882Svi117747 				break;
1282882Svi117747 			}
1292882Svi117747 			last = current;
1302882Svi117747 			current = current->sip_timeout_next;
1312882Svi117747 		}
1322882Svi117747 	}
1332882Svi117747 
1342882Svi117747 	/*
1352882Svi117747 	 * Check if this is in the to-be scheduled list
1362882Svi117747 	 */
1372882Svi117747 	if (!ret && timeout_list != NULL) {
1382882Svi117747 		last = NULL;
1392882Svi117747 		current = timeout_list;
1402882Svi117747 		while (current != NULL) {
1412882Svi117747 			if (current->sip_timeout_id == id) {
1422882Svi117747 				if (current == timeout_list) {
1432882Svi117747 					timeout_list =
1442882Svi117747 					    current->sip_timeout_next;
1452882Svi117747 				} else {
1462882Svi117747 					last->sip_timeout_next =
1472882Svi117747 					    current->sip_timeout_next;
1482882Svi117747 				}
1492882Svi117747 				if (current->sip_timeout_callback_func_arg !=
1502882Svi117747 				    NULL) {
1512882Svi117747 					free(current->
1522882Svi117747 					    sip_timeout_callback_func_arg);
1532882Svi117747 					current->sip_timeout_callback_func_arg =
1542882Svi117747 					    NULL;
1552882Svi117747 				}
1562882Svi117747 				free(current);
1572882Svi117747 				ret = B_TRUE;
1582882Svi117747 				break;
1592882Svi117747 			}
1602882Svi117747 			last = current;
1612882Svi117747 			current = current->sip_timeout_next;
1622882Svi117747 		}
1632882Svi117747 	}
1642882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
1652882Svi117747 	return (ret);
1662882Svi117747 }
1672882Svi117747 
1682882Svi117747 /*
1692882Svi117747  * Add a new timeout
1702882Svi117747  */
1712882Svi117747 uint_t
sip_timeout(void * arg,void (* callback_func)(void *),struct timeval * timeout_time)1722882Svi117747 sip_timeout(void *arg, void (*callback_func)(void *),
1732882Svi117747     struct timeval *timeout_time)
1742882Svi117747 {
1752882Svi117747 	sip_timeout_t	*new_timeout;
176*3439Svi117747 	sip_timeout_t	*current;
177*3439Svi117747 	sip_timeout_t	*last;
1782882Svi117747 	hrtime_t	future_time;
1792882Svi117747 	uint_t		tid;
1802882Svi117747 #ifdef	__linux__
1812882Svi117747 	struct timespec	tspec;
1822882Svi117747 	hrtime_t	now;
1832882Svi117747 #endif
1842882Svi117747 
1852882Svi117747 	new_timeout = malloc(sizeof (sip_timeout_t));
1862882Svi117747 	if (new_timeout == NULL)
1872882Svi117747 		return (0);
1882882Svi117747 
1892882Svi117747 #ifdef	__linux__
1902882Svi117747 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
1912882Svi117747 		return (0);
1922882Svi117747 	now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
1932882Svi117747 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
1942882Svi117747 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
1952882Svi117747 #else
1962882Svi117747 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
1972882Svi117747 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
1982882Svi117747 #endif
1992882Svi117747 	if (future_time <= 0L) {
2002882Svi117747 		free(new_timeout);
2012882Svi117747 		return (0);
2022882Svi117747 	}
2032882Svi117747 
2042882Svi117747 	new_timeout->sip_timeout_next = NULL;
2052882Svi117747 	new_timeout->sip_timeout_val = future_time;
2062882Svi117747 	new_timeout->sip_timeout_callback_func = callback_func;
2072882Svi117747 	new_timeout->sip_timeout_callback_func_arg = arg;
2082882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
2092882Svi117747 	timer_id++;
2102882Svi117747 	if (timer_id == 0)
2112882Svi117747 		timer_id++;
2122882Svi117747 	tid = timer_id;
2132882Svi117747 	new_timeout->sip_timeout_id = tid;
2142882Svi117747 	last = current = timeout_list;
2152882Svi117747 	while (current != NULL) {
2162882Svi117747 		if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
2172882Svi117747 			last = current;
2182882Svi117747 			current = current->sip_timeout_next;
2192882Svi117747 		} else {
2202882Svi117747 			break;
2212882Svi117747 		}
2222882Svi117747 	}
2232882Svi117747 
2242882Svi117747 	if (current == timeout_list) {
2252882Svi117747 		new_timeout->sip_timeout_next  = timeout_list;
2262882Svi117747 		timeout_list = new_timeout;
2272882Svi117747 	} else {
2282882Svi117747 		new_timeout->sip_timeout_next = current,
2292882Svi117747 		last->sip_timeout_next = new_timeout;
2302882Svi117747 	}
2312882Svi117747 	(void) pthread_cond_signal(&timeout_cond_var);
2322882Svi117747 	(void) pthread_mutex_unlock(&timeout_mutex);
2332882Svi117747 	return (tid);
2342882Svi117747 }
2352882Svi117747 
2362882Svi117747 /*
2372882Svi117747  * Schedule the next timeout
2382882Svi117747  */
2392882Svi117747 static hrtime_t
sip_schedule_to_functions()2402882Svi117747 sip_schedule_to_functions()
2412882Svi117747 {
2422882Svi117747 	sip_timeout_t		*timeout = NULL;
2432882Svi117747 	sip_timeout_t		*last = NULL;
2442882Svi117747 	boolean_t		create_thread = B_FALSE;
2452882Svi117747 	hrtime_t		current_time;
2462882Svi117747 #ifdef	__linux__
2472882Svi117747 	struct timespec	tspec;
2482882Svi117747 #endif
2492882Svi117747 
2502882Svi117747 	/*
2512882Svi117747 	 * Thread is holding the mutex.
2522882Svi117747 	 */
2532882Svi117747 #ifdef	__linux__
2542882Svi117747 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
2552882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
2562882Svi117747 	current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
2572882Svi117747 	    tspec.tv_nsec;
2582882Svi117747 #else
2592882Svi117747 	current_time = gethrtime();
2602882Svi117747 #endif
2612882Svi117747 	if (timeout_list == NULL)
2622882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
2632882Svi117747 	timeout = timeout_list;
2642882Svi117747 
2652882Svi117747 	/*
2662882Svi117747 	 * Get all the timeouts that have fired.
2672882Svi117747 	 */
2682882Svi117747 	while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
2692882Svi117747 		last = timeout;
2702882Svi117747 		timeout = timeout->sip_timeout_next;
2712882Svi117747 	}
2722882Svi117747 
2732882Svi117747 	timeout = last;
2742882Svi117747 	if (timeout != NULL) {
2752882Svi117747 		if (timeout_current_end != NULL) {
2762882Svi117747 			timeout_current_end->sip_timeout_next = timeout_list;
2772882Svi117747 			timeout_current_end = timeout;
2782882Svi117747 		} else {
2792882Svi117747 			timeout_current_start = timeout_list;
2802882Svi117747 			timeout_current_end = timeout;
2812882Svi117747 			create_thread = B_TRUE;
2822882Svi117747 		}
2832882Svi117747 		timeout_list = timeout->sip_timeout_next;
2842882Svi117747 		timeout->sip_timeout_next = NULL;
2852882Svi117747 		if (create_thread) {
2862882Svi117747 			pthread_t	thr;
2872882Svi117747 
2882882Svi117747 			(void) pthread_create(&thr, NULL, sip_run_to_functions,
2892882Svi117747 			    NULL);
2902882Svi117747 			(void) pthread_detach(thr);
2912882Svi117747 		}
2922882Svi117747 	}
2932882Svi117747 	if (timeout_list != NULL)
2942882Svi117747 		return (timeout_list->sip_timeout_val);
2952882Svi117747 	else
2962882Svi117747 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
2972882Svi117747 }
2982882Svi117747 
2992882Svi117747 /*
3002882Svi117747  * The timer routine
3012882Svi117747  */
3022882Svi117747 /* ARGSUSED */
3032882Svi117747 static void *
sip_timer_thr(void * arg)3042882Svi117747 sip_timer_thr(void *arg)
3052882Svi117747 {
3062882Svi117747 	timestruc_t	to;
3072882Svi117747 	hrtime_t	current_time;
3082882Svi117747 	hrtime_t	next_timeout;
3092882Svi117747 	hrtime_t	delta;
310*3439Svi117747 	struct timeval tim;
3112882Svi117747 #ifdef	__linux__
3122882Svi117747 	struct timespec	tspec;
3132882Svi117747 #endif
314*3439Svi117747 	delta = (hrtime_t)5 * NANOSEC;
3152882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
3162882Svi117747 	for (;;) {
317*3439Svi117747 		(void) gettimeofday(&tim, NULL);
318*3439Svi117747 		to.tv_sec = tim.tv_sec + (delta / NANOSEC);
319*3439Svi117747 		to.tv_nsec = (hrtime_t)(tim.tv_usec * MILLISEC) +
320*3439Svi117747 		    (delta % NANOSEC);
321*3439Svi117747 		if (to.tv_nsec > NANOSEC) {
322*3439Svi117747 			to.tv_sec += (to.tv_nsec / NANOSEC);
323*3439Svi117747 			to.tv_nsec %= NANOSEC;
324*3439Svi117747 		}
3252882Svi117747 		(void) pthread_cond_timedwait(&timeout_cond_var,
3262882Svi117747 		    &timeout_mutex, &to);
3272882Svi117747 		/*
3282882Svi117747 		 * We return from timedwait because we either timed out
3292882Svi117747 		 * or a new element was added and we need to reset the time
3302882Svi117747 		 */
3312882Svi117747 again:
3322882Svi117747 		next_timeout =  sip_schedule_to_functions();
3332882Svi117747 #ifdef	__linux__
3342882Svi117747 		if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
3352882Svi117747 			goto again; /* ??? */
3362882Svi117747 		current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
3372882Svi117747 		    tspec.tv_nsec;
3382882Svi117747 #else
3392882Svi117747 		current_time = gethrtime();
3402882Svi117747 #endif
3412882Svi117747 		delta = next_timeout - current_time;
3422882Svi117747 		if (delta <= 0)
3432882Svi117747 			goto again;
3442882Svi117747 	}
3452882Svi117747 	/* NOTREACHED */
3462882Svi117747 	return ((void *)0);
3472882Svi117747 }
3482882Svi117747 
3492882Svi117747 /*
3502882Svi117747  * The init routine, starts the timer thread
3512882Svi117747  */
3522882Svi117747 void
sip_timeout_init()3532882Svi117747 sip_timeout_init()
3542882Svi117747 {
3552882Svi117747 	static boolean_t	timout_init = B_FALSE;
3562882Svi117747 	pthread_t		thread1;
3572882Svi117747 
3582882Svi117747 	(void) pthread_mutex_lock(&timeout_mutex);
3592882Svi117747 	if (timout_init == B_FALSE) {
3602882Svi117747 		timout_init = B_TRUE;
3612882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
3622882Svi117747 	} else {
3632882Svi117747 		(void) pthread_mutex_unlock(&timeout_mutex);
3642882Svi117747 		return;
3652882Svi117747 	}
3662882Svi117747 	(void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
3672882Svi117747 }
368