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 * 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 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 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 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 * 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 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