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