xref: /illumos-gate/usr/src/lib/libsip/common/sip_timeout.c (revision 2c2c41837e330b002c4220a39638150db504fe0e)
140cb5e5dSvi /*
240cb5e5dSvi  * CDDL HEADER START
340cb5e5dSvi  *
440cb5e5dSvi  * The contents of this file are subject to the terms of the
540cb5e5dSvi  * Common Development and Distribution License (the "License").
640cb5e5dSvi  * You may not use this file except in compliance with the License.
740cb5e5dSvi  *
840cb5e5dSvi  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
940cb5e5dSvi  * or http://www.opensolaris.org/os/licensing.
1040cb5e5dSvi  * See the License for the specific language governing permissions
1140cb5e5dSvi  * and limitations under the License.
1240cb5e5dSvi  *
1340cb5e5dSvi  * When distributing Covered Code, include this CDDL HEADER in each
1440cb5e5dSvi  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1540cb5e5dSvi  * If applicable, add the following below this CDDL HEADER, with the
1640cb5e5dSvi  * fields enclosed by brackets "[]" replaced with your own identifying
1740cb5e5dSvi  * information: Portions Copyright [yyyy] [name of copyright owner]
1840cb5e5dSvi  *
1940cb5e5dSvi  * CDDL HEADER END
2040cb5e5dSvi  */
2140cb5e5dSvi 
2240cb5e5dSvi /*
23*2c2c4183Svi  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
2440cb5e5dSvi  * Use is subject to license terms.
2540cb5e5dSvi  */
2640cb5e5dSvi 
2740cb5e5dSvi #pragma ident	"%Z%%M%	%I%	%E% SMI"
2840cb5e5dSvi 
2940cb5e5dSvi /*
3040cb5e5dSvi  * Simple implementation of timeout functionality. The granuality is a sec
3140cb5e5dSvi  */
3240cb5e5dSvi #include <pthread.h>
3340cb5e5dSvi #include <stdlib.h>
3440cb5e5dSvi 
3540cb5e5dSvi uint_t		sip_timeout(void *arg, void (*callback_func)(void *),
3640cb5e5dSvi 		    struct timeval *timeout_time);
3740cb5e5dSvi boolean_t	sip_untimeout(uint_t);
3840cb5e5dSvi 
3940cb5e5dSvi typedef struct timeout {
4040cb5e5dSvi 	struct timeout *sip_timeout_next;
4140cb5e5dSvi 	hrtime_t sip_timeout_val;
4240cb5e5dSvi 	void (*sip_timeout_callback_func)(void *);
4340cb5e5dSvi 	void *sip_timeout_callback_func_arg;
4440cb5e5dSvi 	int   sip_timeout_id;
4540cb5e5dSvi } sip_timeout_t;
4640cb5e5dSvi 
4740cb5e5dSvi static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
4840cb5e5dSvi static pthread_cond_t  timeout_cond_var = PTHREAD_COND_INITIALIZER;
4940cb5e5dSvi static sip_timeout_t *timeout_list;
5040cb5e5dSvi static sip_timeout_t *timeout_current_start;
5140cb5e5dSvi static sip_timeout_t *timeout_current_end;
5240cb5e5dSvi 
5340cb5e5dSvi /*
5440cb5e5dSvi  * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
5540cb5e5dSvi  */
5640cb5e5dSvi #define	LONG_SLEEP_TIME	(0x15180LL * 0x3B9ACA00LL)
5740cb5e5dSvi 
5840cb5e5dSvi uint_t timer_id = 0;
5940cb5e5dSvi 
6040cb5e5dSvi /*
6140cb5e5dSvi  * Invoke the callback function
6240cb5e5dSvi  */
6340cb5e5dSvi /* ARGSUSED */
6440cb5e5dSvi static void *
6540cb5e5dSvi sip_run_to_functions(void *arg)
6640cb5e5dSvi {
6740cb5e5dSvi 	sip_timeout_t *timeout = NULL;
6840cb5e5dSvi 
6940cb5e5dSvi 	(void) pthread_mutex_lock(&timeout_mutex);
7040cb5e5dSvi 	while (timeout_current_start != NULL) {
7140cb5e5dSvi 		timeout = timeout_current_start;
7240cb5e5dSvi 		if (timeout_current_end == timeout_current_start)
7340cb5e5dSvi 			timeout_current_start = timeout_current_end = NULL;
7440cb5e5dSvi 		else
7540cb5e5dSvi 			timeout_current_start = timeout->sip_timeout_next;
7640cb5e5dSvi 		(void) pthread_mutex_unlock(&timeout_mutex);
7740cb5e5dSvi 		timeout->sip_timeout_callback_func(
7840cb5e5dSvi 		    timeout->sip_timeout_callback_func_arg);
7940cb5e5dSvi 		free(timeout);
8040cb5e5dSvi 		(void) pthread_mutex_lock(&timeout_mutex);
8140cb5e5dSvi 	}
8240cb5e5dSvi 	(void) pthread_mutex_unlock(&timeout_mutex);
8340cb5e5dSvi 	pthread_exit(NULL);
8440cb5e5dSvi 	return ((void *)0);
8540cb5e5dSvi }
8640cb5e5dSvi 
8740cb5e5dSvi /*
8840cb5e5dSvi  * In the very very unlikely case timer id wraps around and we have two timers
8940cb5e5dSvi  * with the same id. If that happens timer with the least amount of time left
9040cb5e5dSvi  * will be deleted. In case both timers have same time left than the one that
9140cb5e5dSvi  * was scheduled first will be deleted as it will be in the front of the list.
9240cb5e5dSvi  */
9340cb5e5dSvi boolean_t
9440cb5e5dSvi sip_untimeout(uint_t id)
9540cb5e5dSvi {
9640cb5e5dSvi 	boolean_t	ret = B_FALSE;
9740cb5e5dSvi 	sip_timeout_t	*current, *last;
9840cb5e5dSvi 
9940cb5e5dSvi 	last = NULL;
10040cb5e5dSvi 	(void) pthread_mutex_lock(&timeout_mutex);
10140cb5e5dSvi 
10240cb5e5dSvi 	/*
10340cb5e5dSvi 	 * Check if this is in the to-be run list
10440cb5e5dSvi 	 */
10540cb5e5dSvi 	if (timeout_current_start != NULL) {
10640cb5e5dSvi 		current = timeout_current_start;
10740cb5e5dSvi 		while (current != NULL) {
10840cb5e5dSvi 			if (current->sip_timeout_id == id) {
10940cb5e5dSvi 				if (current == timeout_current_start) {
11040cb5e5dSvi 					timeout_current_start =
11140cb5e5dSvi 					    current->sip_timeout_next;
11240cb5e5dSvi 				} else {
11340cb5e5dSvi 					last->sip_timeout_next =
11440cb5e5dSvi 					    current->sip_timeout_next;
11540cb5e5dSvi 				}
11640cb5e5dSvi 				if (current == timeout_current_end)
11740cb5e5dSvi 					timeout_current_end = last;
11840cb5e5dSvi 				if (current->sip_timeout_callback_func_arg !=
11940cb5e5dSvi 				    NULL) {
12040cb5e5dSvi 					free(current->
12140cb5e5dSvi 					    sip_timeout_callback_func_arg);
12240cb5e5dSvi 					current->sip_timeout_callback_func_arg =
12340cb5e5dSvi 					    NULL;
12440cb5e5dSvi 				}
12540cb5e5dSvi 				free(current);
12640cb5e5dSvi 				ret = B_TRUE;
12740cb5e5dSvi 				break;
12840cb5e5dSvi 			}
12940cb5e5dSvi 			last = current;
13040cb5e5dSvi 			current = current->sip_timeout_next;
13140cb5e5dSvi 		}
13240cb5e5dSvi 	}
13340cb5e5dSvi 
13440cb5e5dSvi 	/*
13540cb5e5dSvi 	 * Check if this is in the to-be scheduled list
13640cb5e5dSvi 	 */
13740cb5e5dSvi 	if (!ret && timeout_list != NULL) {
13840cb5e5dSvi 		last = NULL;
13940cb5e5dSvi 		current = timeout_list;
14040cb5e5dSvi 		while (current != NULL) {
14140cb5e5dSvi 			if (current->sip_timeout_id == id) {
14240cb5e5dSvi 				if (current == timeout_list) {
14340cb5e5dSvi 					timeout_list =
14440cb5e5dSvi 					    current->sip_timeout_next;
14540cb5e5dSvi 				} else {
14640cb5e5dSvi 					last->sip_timeout_next =
14740cb5e5dSvi 					    current->sip_timeout_next;
14840cb5e5dSvi 				}
14940cb5e5dSvi 				if (current->sip_timeout_callback_func_arg !=
15040cb5e5dSvi 				    NULL) {
15140cb5e5dSvi 					free(current->
15240cb5e5dSvi 					    sip_timeout_callback_func_arg);
15340cb5e5dSvi 					current->sip_timeout_callback_func_arg =
15440cb5e5dSvi 					    NULL;
15540cb5e5dSvi 				}
15640cb5e5dSvi 				free(current);
15740cb5e5dSvi 				ret = B_TRUE;
15840cb5e5dSvi 				break;
15940cb5e5dSvi 			}
16040cb5e5dSvi 			last = current;
16140cb5e5dSvi 			current = current->sip_timeout_next;
16240cb5e5dSvi 		}
16340cb5e5dSvi 	}
16440cb5e5dSvi 	(void) pthread_mutex_unlock(&timeout_mutex);
16540cb5e5dSvi 	return (ret);
16640cb5e5dSvi }
16740cb5e5dSvi 
16840cb5e5dSvi /*
16940cb5e5dSvi  * Add a new timeout
17040cb5e5dSvi  */
17140cb5e5dSvi uint_t
17240cb5e5dSvi sip_timeout(void *arg, void (*callback_func)(void *),
17340cb5e5dSvi     struct timeval *timeout_time)
17440cb5e5dSvi {
17540cb5e5dSvi 	sip_timeout_t	*new_timeout;
176*2c2c4183Svi 	sip_timeout_t	*current;
177*2c2c4183Svi 	sip_timeout_t	*last;
17840cb5e5dSvi 	hrtime_t	future_time;
17940cb5e5dSvi 	uint_t		tid;
18040cb5e5dSvi #ifdef	__linux__
18140cb5e5dSvi 	struct timespec	tspec;
18240cb5e5dSvi 	hrtime_t	now;
18340cb5e5dSvi #endif
18440cb5e5dSvi 
18540cb5e5dSvi 	new_timeout = malloc(sizeof (sip_timeout_t));
18640cb5e5dSvi 	if (new_timeout == NULL)
18740cb5e5dSvi 		return (0);
18840cb5e5dSvi 
18940cb5e5dSvi #ifdef	__linux__
19040cb5e5dSvi 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
19140cb5e5dSvi 		return (0);
19240cb5e5dSvi 	now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
19340cb5e5dSvi 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
19440cb5e5dSvi 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
19540cb5e5dSvi #else
19640cb5e5dSvi 	future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
19740cb5e5dSvi 	    (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
19840cb5e5dSvi #endif
19940cb5e5dSvi 	if (future_time <= 0L) {
20040cb5e5dSvi 		free(new_timeout);
20140cb5e5dSvi 		return (0);
20240cb5e5dSvi 	}
20340cb5e5dSvi 
20440cb5e5dSvi 	new_timeout->sip_timeout_next = NULL;
20540cb5e5dSvi 	new_timeout->sip_timeout_val = future_time;
20640cb5e5dSvi 	new_timeout->sip_timeout_callback_func = callback_func;
20740cb5e5dSvi 	new_timeout->sip_timeout_callback_func_arg = arg;
20840cb5e5dSvi 	(void) pthread_mutex_lock(&timeout_mutex);
20940cb5e5dSvi 	timer_id++;
21040cb5e5dSvi 	if (timer_id == 0)
21140cb5e5dSvi 		timer_id++;
21240cb5e5dSvi 	tid = timer_id;
21340cb5e5dSvi 	new_timeout->sip_timeout_id = tid;
21440cb5e5dSvi 	last = current = timeout_list;
21540cb5e5dSvi 	while (current != NULL) {
21640cb5e5dSvi 		if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
21740cb5e5dSvi 			last = current;
21840cb5e5dSvi 			current = current->sip_timeout_next;
21940cb5e5dSvi 		} else {
22040cb5e5dSvi 			break;
22140cb5e5dSvi 		}
22240cb5e5dSvi 	}
22340cb5e5dSvi 
22440cb5e5dSvi 	if (current == timeout_list) {
22540cb5e5dSvi 		new_timeout->sip_timeout_next  = timeout_list;
22640cb5e5dSvi 		timeout_list = new_timeout;
22740cb5e5dSvi 	} else {
22840cb5e5dSvi 		new_timeout->sip_timeout_next = current,
22940cb5e5dSvi 		last->sip_timeout_next = new_timeout;
23040cb5e5dSvi 	}
23140cb5e5dSvi 	(void) pthread_cond_signal(&timeout_cond_var);
23240cb5e5dSvi 	(void) pthread_mutex_unlock(&timeout_mutex);
23340cb5e5dSvi 	return (tid);
23440cb5e5dSvi }
23540cb5e5dSvi 
23640cb5e5dSvi /*
23740cb5e5dSvi  * Schedule the next timeout
23840cb5e5dSvi  */
23940cb5e5dSvi static hrtime_t
24040cb5e5dSvi sip_schedule_to_functions()
24140cb5e5dSvi {
24240cb5e5dSvi 	sip_timeout_t		*timeout = NULL;
24340cb5e5dSvi 	sip_timeout_t		*last = NULL;
24440cb5e5dSvi 	boolean_t		create_thread = B_FALSE;
24540cb5e5dSvi 	hrtime_t		current_time;
24640cb5e5dSvi #ifdef	__linux__
24740cb5e5dSvi 	struct timespec	tspec;
24840cb5e5dSvi #endif
24940cb5e5dSvi 
25040cb5e5dSvi 	/*
25140cb5e5dSvi 	 * Thread is holding the mutex.
25240cb5e5dSvi 	 */
25340cb5e5dSvi #ifdef	__linux__
25440cb5e5dSvi 	if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
25540cb5e5dSvi 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
25640cb5e5dSvi 	current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
25740cb5e5dSvi 	    tspec.tv_nsec;
25840cb5e5dSvi #else
25940cb5e5dSvi 	current_time = gethrtime();
26040cb5e5dSvi #endif
26140cb5e5dSvi 	if (timeout_list == NULL)
26240cb5e5dSvi 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
26340cb5e5dSvi 	timeout = timeout_list;
26440cb5e5dSvi 
26540cb5e5dSvi 	/*
26640cb5e5dSvi 	 * Get all the timeouts that have fired.
26740cb5e5dSvi 	 */
26840cb5e5dSvi 	while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
26940cb5e5dSvi 		last = timeout;
27040cb5e5dSvi 		timeout = timeout->sip_timeout_next;
27140cb5e5dSvi 	}
27240cb5e5dSvi 
27340cb5e5dSvi 	timeout = last;
27440cb5e5dSvi 	if (timeout != NULL) {
27540cb5e5dSvi 		if (timeout_current_end != NULL) {
27640cb5e5dSvi 			timeout_current_end->sip_timeout_next = timeout_list;
27740cb5e5dSvi 			timeout_current_end = timeout;
27840cb5e5dSvi 		} else {
27940cb5e5dSvi 			timeout_current_start = timeout_list;
28040cb5e5dSvi 			timeout_current_end = timeout;
28140cb5e5dSvi 			create_thread = B_TRUE;
28240cb5e5dSvi 		}
28340cb5e5dSvi 		timeout_list = timeout->sip_timeout_next;
28440cb5e5dSvi 		timeout->sip_timeout_next = NULL;
28540cb5e5dSvi 		if (create_thread) {
28640cb5e5dSvi 			pthread_t	thr;
28740cb5e5dSvi 
28840cb5e5dSvi 			(void) pthread_create(&thr, NULL, sip_run_to_functions,
28940cb5e5dSvi 			    NULL);
29040cb5e5dSvi 			(void) pthread_detach(thr);
29140cb5e5dSvi 		}
29240cb5e5dSvi 	}
29340cb5e5dSvi 	if (timeout_list != NULL)
29440cb5e5dSvi 		return (timeout_list->sip_timeout_val);
29540cb5e5dSvi 	else
29640cb5e5dSvi 		return ((hrtime_t)LONG_SLEEP_TIME + current_time);
29740cb5e5dSvi }
29840cb5e5dSvi 
29940cb5e5dSvi /*
30040cb5e5dSvi  * The timer routine
30140cb5e5dSvi  */
30240cb5e5dSvi /* ARGSUSED */
30340cb5e5dSvi static void *
30440cb5e5dSvi sip_timer_thr(void *arg)
30540cb5e5dSvi {
30640cb5e5dSvi 	timestruc_t	to;
30740cb5e5dSvi 	hrtime_t	current_time;
30840cb5e5dSvi 	hrtime_t	next_timeout;
30940cb5e5dSvi 	hrtime_t	delta;
310*2c2c4183Svi 	struct timeval tim;
31140cb5e5dSvi #ifdef	__linux__
31240cb5e5dSvi 	struct timespec	tspec;
31340cb5e5dSvi #endif
314*2c2c4183Svi 	delta = (hrtime_t)5 * NANOSEC;
31540cb5e5dSvi 	(void) pthread_mutex_lock(&timeout_mutex);
31640cb5e5dSvi 	for (;;) {
317*2c2c4183Svi 		(void) gettimeofday(&tim, NULL);
318*2c2c4183Svi 		to.tv_sec = tim.tv_sec + (delta / NANOSEC);
319*2c2c4183Svi 		to.tv_nsec = (hrtime_t)(tim.tv_usec * MILLISEC) +
320*2c2c4183Svi 		    (delta % NANOSEC);
321*2c2c4183Svi 		if (to.tv_nsec > NANOSEC) {
322*2c2c4183Svi 			to.tv_sec += (to.tv_nsec / NANOSEC);
323*2c2c4183Svi 			to.tv_nsec %= NANOSEC;
324*2c2c4183Svi 		}
32540cb5e5dSvi 		(void) pthread_cond_timedwait(&timeout_cond_var,
32640cb5e5dSvi 		    &timeout_mutex, &to);
32740cb5e5dSvi 		/*
32840cb5e5dSvi 		 * We return from timedwait because we either timed out
32940cb5e5dSvi 		 * or a new element was added and we need to reset the time
33040cb5e5dSvi 		 */
33140cb5e5dSvi again:
33240cb5e5dSvi 		next_timeout =  sip_schedule_to_functions();
33340cb5e5dSvi #ifdef	__linux__
33440cb5e5dSvi 		if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
33540cb5e5dSvi 			goto again; /* ??? */
33640cb5e5dSvi 		current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
33740cb5e5dSvi 		    tspec.tv_nsec;
33840cb5e5dSvi #else
33940cb5e5dSvi 		current_time = gethrtime();
34040cb5e5dSvi #endif
34140cb5e5dSvi 		delta = next_timeout - current_time;
34240cb5e5dSvi 		if (delta <= 0)
34340cb5e5dSvi 			goto again;
34440cb5e5dSvi 	}
34540cb5e5dSvi 	/* NOTREACHED */
34640cb5e5dSvi 	return ((void *)0);
34740cb5e5dSvi }
34840cb5e5dSvi 
34940cb5e5dSvi /*
35040cb5e5dSvi  * The init routine, starts the timer thread
35140cb5e5dSvi  */
35240cb5e5dSvi void
35340cb5e5dSvi sip_timeout_init()
35440cb5e5dSvi {
35540cb5e5dSvi 	static boolean_t	timout_init = B_FALSE;
35640cb5e5dSvi 	pthread_t		thread1;
35740cb5e5dSvi 
35840cb5e5dSvi 	(void) pthread_mutex_lock(&timeout_mutex);
35940cb5e5dSvi 	if (timout_init == B_FALSE) {
36040cb5e5dSvi 		timout_init = B_TRUE;
36140cb5e5dSvi 		(void) pthread_mutex_unlock(&timeout_mutex);
36240cb5e5dSvi 	} else {
36340cb5e5dSvi 		(void) pthread_mutex_unlock(&timeout_mutex);
36440cb5e5dSvi 		return;
36540cb5e5dSvi 	}
36640cb5e5dSvi 	(void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
36740cb5e5dSvi }
368