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 /*
2840cb5e5dSvi * Simple implementation of timeout functionality. The granuality is a sec
2940cb5e5dSvi */
3040cb5e5dSvi #include <pthread.h>
3140cb5e5dSvi #include <stdlib.h>
3240cb5e5dSvi
3340cb5e5dSvi uint_t sip_timeout(void *arg, void (*callback_func)(void *),
3440cb5e5dSvi struct timeval *timeout_time);
3540cb5e5dSvi boolean_t sip_untimeout(uint_t);
3640cb5e5dSvi
3740cb5e5dSvi typedef struct timeout {
3840cb5e5dSvi struct timeout *sip_timeout_next;
3940cb5e5dSvi hrtime_t sip_timeout_val;
4040cb5e5dSvi void (*sip_timeout_callback_func)(void *);
4140cb5e5dSvi void *sip_timeout_callback_func_arg;
4240cb5e5dSvi int sip_timeout_id;
4340cb5e5dSvi } sip_timeout_t;
4440cb5e5dSvi
4540cb5e5dSvi static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER;
4640cb5e5dSvi static pthread_cond_t timeout_cond_var = PTHREAD_COND_INITIALIZER;
4740cb5e5dSvi static sip_timeout_t *timeout_list;
4840cb5e5dSvi static sip_timeout_t *timeout_current_start;
4940cb5e5dSvi static sip_timeout_t *timeout_current_end;
5040cb5e5dSvi
5140cb5e5dSvi /*
5240cb5e5dSvi * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC)
5340cb5e5dSvi */
5440cb5e5dSvi #define LONG_SLEEP_TIME (0x15180LL * 0x3B9ACA00LL)
5540cb5e5dSvi
5640cb5e5dSvi uint_t timer_id = 0;
5740cb5e5dSvi
5840cb5e5dSvi /*
5940cb5e5dSvi * Invoke the callback function
6040cb5e5dSvi */
6140cb5e5dSvi /* ARGSUSED */
6240cb5e5dSvi static void *
sip_run_to_functions(void * arg)6340cb5e5dSvi sip_run_to_functions(void *arg)
6440cb5e5dSvi {
6540cb5e5dSvi sip_timeout_t *timeout = NULL;
6640cb5e5dSvi
6740cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
6840cb5e5dSvi while (timeout_current_start != NULL) {
6940cb5e5dSvi timeout = timeout_current_start;
7040cb5e5dSvi if (timeout_current_end == timeout_current_start)
7140cb5e5dSvi timeout_current_start = timeout_current_end = NULL;
7240cb5e5dSvi else
7340cb5e5dSvi timeout_current_start = timeout->sip_timeout_next;
7440cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
7540cb5e5dSvi timeout->sip_timeout_callback_func(
7640cb5e5dSvi timeout->sip_timeout_callback_func_arg);
7740cb5e5dSvi free(timeout);
7840cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
7940cb5e5dSvi }
8040cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
8140cb5e5dSvi pthread_exit(NULL);
8240cb5e5dSvi return ((void *)0);
8340cb5e5dSvi }
8440cb5e5dSvi
8540cb5e5dSvi /*
8640cb5e5dSvi * In the very very unlikely case timer id wraps around and we have two timers
8740cb5e5dSvi * with the same id. If that happens timer with the least amount of time left
8840cb5e5dSvi * will be deleted. In case both timers have same time left than the one that
8940cb5e5dSvi * was scheduled first will be deleted as it will be in the front of the list.
9040cb5e5dSvi */
9140cb5e5dSvi boolean_t
sip_untimeout(uint_t id)9240cb5e5dSvi sip_untimeout(uint_t id)
9340cb5e5dSvi {
9440cb5e5dSvi boolean_t ret = B_FALSE;
9540cb5e5dSvi sip_timeout_t *current, *last;
9640cb5e5dSvi
9740cb5e5dSvi last = NULL;
9840cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
9940cb5e5dSvi
10040cb5e5dSvi /*
10140cb5e5dSvi * Check if this is in the to-be run list
10240cb5e5dSvi */
10340cb5e5dSvi if (timeout_current_start != NULL) {
10440cb5e5dSvi current = timeout_current_start;
10540cb5e5dSvi while (current != NULL) {
10640cb5e5dSvi if (current->sip_timeout_id == id) {
10740cb5e5dSvi if (current == timeout_current_start) {
10840cb5e5dSvi timeout_current_start =
10940cb5e5dSvi current->sip_timeout_next;
11040cb5e5dSvi } else {
11140cb5e5dSvi last->sip_timeout_next =
11240cb5e5dSvi current->sip_timeout_next;
11340cb5e5dSvi }
11440cb5e5dSvi if (current == timeout_current_end)
11540cb5e5dSvi timeout_current_end = last;
11640cb5e5dSvi if (current->sip_timeout_callback_func_arg !=
11740cb5e5dSvi NULL) {
11840cb5e5dSvi free(current->
11940cb5e5dSvi sip_timeout_callback_func_arg);
12040cb5e5dSvi current->sip_timeout_callback_func_arg =
12140cb5e5dSvi NULL;
12240cb5e5dSvi }
12340cb5e5dSvi free(current);
12440cb5e5dSvi ret = B_TRUE;
12540cb5e5dSvi break;
12640cb5e5dSvi }
12740cb5e5dSvi last = current;
12840cb5e5dSvi current = current->sip_timeout_next;
12940cb5e5dSvi }
13040cb5e5dSvi }
13140cb5e5dSvi
13240cb5e5dSvi /*
13340cb5e5dSvi * Check if this is in the to-be scheduled list
13440cb5e5dSvi */
13540cb5e5dSvi if (!ret && timeout_list != NULL) {
13640cb5e5dSvi last = NULL;
13740cb5e5dSvi current = timeout_list;
13840cb5e5dSvi while (current != NULL) {
13940cb5e5dSvi if (current->sip_timeout_id == id) {
14040cb5e5dSvi if (current == timeout_list) {
14140cb5e5dSvi timeout_list =
14240cb5e5dSvi current->sip_timeout_next;
14340cb5e5dSvi } else {
14440cb5e5dSvi last->sip_timeout_next =
14540cb5e5dSvi current->sip_timeout_next;
14640cb5e5dSvi }
14740cb5e5dSvi if (current->sip_timeout_callback_func_arg !=
14840cb5e5dSvi NULL) {
14940cb5e5dSvi free(current->
15040cb5e5dSvi sip_timeout_callback_func_arg);
15140cb5e5dSvi current->sip_timeout_callback_func_arg =
15240cb5e5dSvi NULL;
15340cb5e5dSvi }
15440cb5e5dSvi free(current);
15540cb5e5dSvi ret = B_TRUE;
15640cb5e5dSvi break;
15740cb5e5dSvi }
15840cb5e5dSvi last = current;
15940cb5e5dSvi current = current->sip_timeout_next;
16040cb5e5dSvi }
16140cb5e5dSvi }
16240cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
16340cb5e5dSvi return (ret);
16440cb5e5dSvi }
16540cb5e5dSvi
16640cb5e5dSvi /*
16740cb5e5dSvi * Add a new timeout
16840cb5e5dSvi */
16940cb5e5dSvi uint_t
sip_timeout(void * arg,void (* callback_func)(void *),struct timeval * timeout_time)17040cb5e5dSvi sip_timeout(void *arg, void (*callback_func)(void *),
17140cb5e5dSvi struct timeval *timeout_time)
17240cb5e5dSvi {
17340cb5e5dSvi sip_timeout_t *new_timeout;
174*2c2c4183Svi sip_timeout_t *current;
175*2c2c4183Svi sip_timeout_t *last;
17640cb5e5dSvi hrtime_t future_time;
17740cb5e5dSvi uint_t tid;
17840cb5e5dSvi #ifdef __linux__
17940cb5e5dSvi struct timespec tspec;
18040cb5e5dSvi hrtime_t now;
18140cb5e5dSvi #endif
18240cb5e5dSvi
18340cb5e5dSvi new_timeout = malloc(sizeof (sip_timeout_t));
18440cb5e5dSvi if (new_timeout == NULL)
18540cb5e5dSvi return (0);
18640cb5e5dSvi
18740cb5e5dSvi #ifdef __linux__
18840cb5e5dSvi if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
18940cb5e5dSvi return (0);
19040cb5e5dSvi now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec;
19140cb5e5dSvi future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
19240cb5e5dSvi (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now;
19340cb5e5dSvi #else
19440cb5e5dSvi future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC +
19540cb5e5dSvi (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime();
19640cb5e5dSvi #endif
19740cb5e5dSvi if (future_time <= 0L) {
19840cb5e5dSvi free(new_timeout);
19940cb5e5dSvi return (0);
20040cb5e5dSvi }
20140cb5e5dSvi
20240cb5e5dSvi new_timeout->sip_timeout_next = NULL;
20340cb5e5dSvi new_timeout->sip_timeout_val = future_time;
20440cb5e5dSvi new_timeout->sip_timeout_callback_func = callback_func;
20540cb5e5dSvi new_timeout->sip_timeout_callback_func_arg = arg;
20640cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
20740cb5e5dSvi timer_id++;
20840cb5e5dSvi if (timer_id == 0)
20940cb5e5dSvi timer_id++;
21040cb5e5dSvi tid = timer_id;
21140cb5e5dSvi new_timeout->sip_timeout_id = tid;
21240cb5e5dSvi last = current = timeout_list;
21340cb5e5dSvi while (current != NULL) {
21440cb5e5dSvi if (current->sip_timeout_val <= new_timeout->sip_timeout_val) {
21540cb5e5dSvi last = current;
21640cb5e5dSvi current = current->sip_timeout_next;
21740cb5e5dSvi } else {
21840cb5e5dSvi break;
21940cb5e5dSvi }
22040cb5e5dSvi }
22140cb5e5dSvi
22240cb5e5dSvi if (current == timeout_list) {
22340cb5e5dSvi new_timeout->sip_timeout_next = timeout_list;
22440cb5e5dSvi timeout_list = new_timeout;
22540cb5e5dSvi } else {
22640cb5e5dSvi new_timeout->sip_timeout_next = current,
22740cb5e5dSvi last->sip_timeout_next = new_timeout;
22840cb5e5dSvi }
22940cb5e5dSvi (void) pthread_cond_signal(&timeout_cond_var);
23040cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
23140cb5e5dSvi return (tid);
23240cb5e5dSvi }
23340cb5e5dSvi
23440cb5e5dSvi /*
23540cb5e5dSvi * Schedule the next timeout
23640cb5e5dSvi */
23740cb5e5dSvi static hrtime_t
sip_schedule_to_functions()23840cb5e5dSvi sip_schedule_to_functions()
23940cb5e5dSvi {
24040cb5e5dSvi sip_timeout_t *timeout = NULL;
24140cb5e5dSvi sip_timeout_t *last = NULL;
24240cb5e5dSvi boolean_t create_thread = B_FALSE;
24340cb5e5dSvi hrtime_t current_time;
24440cb5e5dSvi #ifdef __linux__
24540cb5e5dSvi struct timespec tspec;
24640cb5e5dSvi #endif
24740cb5e5dSvi
24840cb5e5dSvi /*
24940cb5e5dSvi * Thread is holding the mutex.
25040cb5e5dSvi */
25140cb5e5dSvi #ifdef __linux__
25240cb5e5dSvi if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
25340cb5e5dSvi return ((hrtime_t)LONG_SLEEP_TIME + current_time);
25440cb5e5dSvi current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
25540cb5e5dSvi tspec.tv_nsec;
25640cb5e5dSvi #else
25740cb5e5dSvi current_time = gethrtime();
25840cb5e5dSvi #endif
25940cb5e5dSvi if (timeout_list == NULL)
26040cb5e5dSvi return ((hrtime_t)LONG_SLEEP_TIME + current_time);
26140cb5e5dSvi timeout = timeout_list;
26240cb5e5dSvi
26340cb5e5dSvi /*
26440cb5e5dSvi * Get all the timeouts that have fired.
26540cb5e5dSvi */
26640cb5e5dSvi while (timeout != NULL && timeout->sip_timeout_val <= current_time) {
26740cb5e5dSvi last = timeout;
26840cb5e5dSvi timeout = timeout->sip_timeout_next;
26940cb5e5dSvi }
27040cb5e5dSvi
27140cb5e5dSvi timeout = last;
27240cb5e5dSvi if (timeout != NULL) {
27340cb5e5dSvi if (timeout_current_end != NULL) {
27440cb5e5dSvi timeout_current_end->sip_timeout_next = timeout_list;
27540cb5e5dSvi timeout_current_end = timeout;
27640cb5e5dSvi } else {
27740cb5e5dSvi timeout_current_start = timeout_list;
27840cb5e5dSvi timeout_current_end = timeout;
27940cb5e5dSvi create_thread = B_TRUE;
28040cb5e5dSvi }
28140cb5e5dSvi timeout_list = timeout->sip_timeout_next;
28240cb5e5dSvi timeout->sip_timeout_next = NULL;
28340cb5e5dSvi if (create_thread) {
28440cb5e5dSvi pthread_t thr;
28540cb5e5dSvi
28640cb5e5dSvi (void) pthread_create(&thr, NULL, sip_run_to_functions,
28740cb5e5dSvi NULL);
28840cb5e5dSvi (void) pthread_detach(thr);
28940cb5e5dSvi }
29040cb5e5dSvi }
29140cb5e5dSvi if (timeout_list != NULL)
29240cb5e5dSvi return (timeout_list->sip_timeout_val);
29340cb5e5dSvi else
29440cb5e5dSvi return ((hrtime_t)LONG_SLEEP_TIME + current_time);
29540cb5e5dSvi }
29640cb5e5dSvi
29740cb5e5dSvi /*
29840cb5e5dSvi * The timer routine
29940cb5e5dSvi */
30040cb5e5dSvi /* ARGSUSED */
30140cb5e5dSvi static void *
sip_timer_thr(void * arg)30240cb5e5dSvi sip_timer_thr(void *arg)
30340cb5e5dSvi {
30440cb5e5dSvi timestruc_t to;
30540cb5e5dSvi hrtime_t current_time;
30640cb5e5dSvi hrtime_t next_timeout;
30740cb5e5dSvi hrtime_t delta;
308*2c2c4183Svi struct timeval tim;
30940cb5e5dSvi #ifdef __linux__
31040cb5e5dSvi struct timespec tspec;
31140cb5e5dSvi #endif
312*2c2c4183Svi delta = (hrtime_t)5 * NANOSEC;
31340cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
31440cb5e5dSvi for (;;) {
315*2c2c4183Svi (void) gettimeofday(&tim, NULL);
316*2c2c4183Svi to.tv_sec = tim.tv_sec + (delta / NANOSEC);
317*2c2c4183Svi to.tv_nsec = (hrtime_t)(tim.tv_usec * MILLISEC) +
318*2c2c4183Svi (delta % NANOSEC);
319*2c2c4183Svi if (to.tv_nsec > NANOSEC) {
320*2c2c4183Svi to.tv_sec += (to.tv_nsec / NANOSEC);
321*2c2c4183Svi to.tv_nsec %= NANOSEC;
322*2c2c4183Svi }
32340cb5e5dSvi (void) pthread_cond_timedwait(&timeout_cond_var,
32440cb5e5dSvi &timeout_mutex, &to);
32540cb5e5dSvi /*
32640cb5e5dSvi * We return from timedwait because we either timed out
32740cb5e5dSvi * or a new element was added and we need to reset the time
32840cb5e5dSvi */
32940cb5e5dSvi again:
33040cb5e5dSvi next_timeout = sip_schedule_to_functions();
33140cb5e5dSvi #ifdef __linux__
33240cb5e5dSvi if (clock_gettime(CLOCK_REALTIME, &tspec) != 0)
33340cb5e5dSvi goto again; /* ??? */
33440cb5e5dSvi current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC +
33540cb5e5dSvi tspec.tv_nsec;
33640cb5e5dSvi #else
33740cb5e5dSvi current_time = gethrtime();
33840cb5e5dSvi #endif
33940cb5e5dSvi delta = next_timeout - current_time;
34040cb5e5dSvi if (delta <= 0)
34140cb5e5dSvi goto again;
34240cb5e5dSvi }
34340cb5e5dSvi /* NOTREACHED */
34440cb5e5dSvi return ((void *)0);
34540cb5e5dSvi }
34640cb5e5dSvi
34740cb5e5dSvi /*
34840cb5e5dSvi * The init routine, starts the timer thread
34940cb5e5dSvi */
35040cb5e5dSvi void
sip_timeout_init()35140cb5e5dSvi sip_timeout_init()
35240cb5e5dSvi {
35340cb5e5dSvi static boolean_t timout_init = B_FALSE;
35440cb5e5dSvi pthread_t thread1;
35540cb5e5dSvi
35640cb5e5dSvi (void) pthread_mutex_lock(&timeout_mutex);
35740cb5e5dSvi if (timout_init == B_FALSE) {
35840cb5e5dSvi timout_init = B_TRUE;
35940cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
36040cb5e5dSvi } else {
36140cb5e5dSvi (void) pthread_mutex_unlock(&timeout_mutex);
36240cb5e5dSvi return;
36340cb5e5dSvi }
36440cb5e5dSvi (void) pthread_create(&thread1, NULL, sip_timer_thr, NULL);
36540cb5e5dSvi }
366