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