12882Svi117747 /* 22882Svi117747 * CDDL HEADER START 32882Svi117747 * 42882Svi117747 * The contents of this file are subject to the terms of the 52882Svi117747 * Common Development and Distribution License (the "License"). 62882Svi117747 * You may not use this file except in compliance with the License. 72882Svi117747 * 82882Svi117747 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92882Svi117747 * or http://www.opensolaris.org/os/licensing. 102882Svi117747 * See the License for the specific language governing permissions 112882Svi117747 * and limitations under the License. 122882Svi117747 * 132882Svi117747 * When distributing Covered Code, include this CDDL HEADER in each 142882Svi117747 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152882Svi117747 * If applicable, add the following below this CDDL HEADER, with the 162882Svi117747 * fields enclosed by brackets "[]" replaced with your own identifying 172882Svi117747 * information: Portions Copyright [yyyy] [name of copyright owner] 182882Svi117747 * 192882Svi117747 * CDDL HEADER END 202882Svi117747 */ 212882Svi117747 222882Svi117747 /* 23*3439Svi117747 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 242882Svi117747 * Use is subject to license terms. 252882Svi117747 */ 262882Svi117747 272882Svi117747 #pragma ident "%Z%%M% %I% %E% SMI" 282882Svi117747 292882Svi117747 /* 302882Svi117747 * Simple implementation of timeout functionality. The granuality is a sec 312882Svi117747 */ 322882Svi117747 #include <pthread.h> 332882Svi117747 #include <stdlib.h> 342882Svi117747 352882Svi117747 uint_t sip_timeout(void *arg, void (*callback_func)(void *), 362882Svi117747 struct timeval *timeout_time); 372882Svi117747 boolean_t sip_untimeout(uint_t); 382882Svi117747 392882Svi117747 typedef struct timeout { 402882Svi117747 struct timeout *sip_timeout_next; 412882Svi117747 hrtime_t sip_timeout_val; 422882Svi117747 void (*sip_timeout_callback_func)(void *); 432882Svi117747 void *sip_timeout_callback_func_arg; 442882Svi117747 int sip_timeout_id; 452882Svi117747 } sip_timeout_t; 462882Svi117747 472882Svi117747 static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER; 482882Svi117747 static pthread_cond_t timeout_cond_var = PTHREAD_COND_INITIALIZER; 492882Svi117747 static sip_timeout_t *timeout_list; 502882Svi117747 static sip_timeout_t *timeout_current_start; 512882Svi117747 static sip_timeout_t *timeout_current_end; 522882Svi117747 532882Svi117747 /* 542882Svi117747 * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC) 552882Svi117747 */ 562882Svi117747 #define LONG_SLEEP_TIME (0x15180LL * 0x3B9ACA00LL) 572882Svi117747 582882Svi117747 uint_t timer_id = 0; 592882Svi117747 602882Svi117747 /* 612882Svi117747 * Invoke the callback function 622882Svi117747 */ 632882Svi117747 /* ARGSUSED */ 642882Svi117747 static void * 652882Svi117747 sip_run_to_functions(void *arg) 662882Svi117747 { 672882Svi117747 sip_timeout_t *timeout = NULL; 682882Svi117747 692882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 702882Svi117747 while (timeout_current_start != NULL) { 712882Svi117747 timeout = timeout_current_start; 722882Svi117747 if (timeout_current_end == timeout_current_start) 732882Svi117747 timeout_current_start = timeout_current_end = NULL; 742882Svi117747 else 752882Svi117747 timeout_current_start = timeout->sip_timeout_next; 762882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 772882Svi117747 timeout->sip_timeout_callback_func( 782882Svi117747 timeout->sip_timeout_callback_func_arg); 792882Svi117747 free(timeout); 802882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 812882Svi117747 } 822882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 832882Svi117747 pthread_exit(NULL); 842882Svi117747 return ((void *)0); 852882Svi117747 } 862882Svi117747 872882Svi117747 /* 882882Svi117747 * In the very very unlikely case timer id wraps around and we have two timers 892882Svi117747 * with the same id. If that happens timer with the least amount of time left 902882Svi117747 * will be deleted. In case both timers have same time left than the one that 912882Svi117747 * was scheduled first will be deleted as it will be in the front of the list. 922882Svi117747 */ 932882Svi117747 boolean_t 942882Svi117747 sip_untimeout(uint_t id) 952882Svi117747 { 962882Svi117747 boolean_t ret = B_FALSE; 972882Svi117747 sip_timeout_t *current, *last; 982882Svi117747 992882Svi117747 last = NULL; 1002882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 1012882Svi117747 1022882Svi117747 /* 1032882Svi117747 * Check if this is in the to-be run list 1042882Svi117747 */ 1052882Svi117747 if (timeout_current_start != NULL) { 1062882Svi117747 current = timeout_current_start; 1072882Svi117747 while (current != NULL) { 1082882Svi117747 if (current->sip_timeout_id == id) { 1092882Svi117747 if (current == timeout_current_start) { 1102882Svi117747 timeout_current_start = 1112882Svi117747 current->sip_timeout_next; 1122882Svi117747 } else { 1132882Svi117747 last->sip_timeout_next = 1142882Svi117747 current->sip_timeout_next; 1152882Svi117747 } 1162882Svi117747 if (current == timeout_current_end) 1172882Svi117747 timeout_current_end = last; 1182882Svi117747 if (current->sip_timeout_callback_func_arg != 1192882Svi117747 NULL) { 1202882Svi117747 free(current-> 1212882Svi117747 sip_timeout_callback_func_arg); 1222882Svi117747 current->sip_timeout_callback_func_arg = 1232882Svi117747 NULL; 1242882Svi117747 } 1252882Svi117747 free(current); 1262882Svi117747 ret = B_TRUE; 1272882Svi117747 break; 1282882Svi117747 } 1292882Svi117747 last = current; 1302882Svi117747 current = current->sip_timeout_next; 1312882Svi117747 } 1322882Svi117747 } 1332882Svi117747 1342882Svi117747 /* 1352882Svi117747 * Check if this is in the to-be scheduled list 1362882Svi117747 */ 1372882Svi117747 if (!ret && timeout_list != NULL) { 1382882Svi117747 last = NULL; 1392882Svi117747 current = timeout_list; 1402882Svi117747 while (current != NULL) { 1412882Svi117747 if (current->sip_timeout_id == id) { 1422882Svi117747 if (current == timeout_list) { 1432882Svi117747 timeout_list = 1442882Svi117747 current->sip_timeout_next; 1452882Svi117747 } else { 1462882Svi117747 last->sip_timeout_next = 1472882Svi117747 current->sip_timeout_next; 1482882Svi117747 } 1492882Svi117747 if (current->sip_timeout_callback_func_arg != 1502882Svi117747 NULL) { 1512882Svi117747 free(current-> 1522882Svi117747 sip_timeout_callback_func_arg); 1532882Svi117747 current->sip_timeout_callback_func_arg = 1542882Svi117747 NULL; 1552882Svi117747 } 1562882Svi117747 free(current); 1572882Svi117747 ret = B_TRUE; 1582882Svi117747 break; 1592882Svi117747 } 1602882Svi117747 last = current; 1612882Svi117747 current = current->sip_timeout_next; 1622882Svi117747 } 1632882Svi117747 } 1642882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 1652882Svi117747 return (ret); 1662882Svi117747 } 1672882Svi117747 1682882Svi117747 /* 1692882Svi117747 * Add a new timeout 1702882Svi117747 */ 1712882Svi117747 uint_t 1722882Svi117747 sip_timeout(void *arg, void (*callback_func)(void *), 1732882Svi117747 struct timeval *timeout_time) 1742882Svi117747 { 1752882Svi117747 sip_timeout_t *new_timeout; 176*3439Svi117747 sip_timeout_t *current; 177*3439Svi117747 sip_timeout_t *last; 1782882Svi117747 hrtime_t future_time; 1792882Svi117747 uint_t tid; 1802882Svi117747 #ifdef __linux__ 1812882Svi117747 struct timespec tspec; 1822882Svi117747 hrtime_t now; 1832882Svi117747 #endif 1842882Svi117747 1852882Svi117747 new_timeout = malloc(sizeof (sip_timeout_t)); 1862882Svi117747 if (new_timeout == NULL) 1872882Svi117747 return (0); 1882882Svi117747 1892882Svi117747 #ifdef __linux__ 1902882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 1912882Svi117747 return (0); 1922882Svi117747 now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec; 1932882Svi117747 future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC + 1942882Svi117747 (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now; 1952882Svi117747 #else 1962882Svi117747 future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC + 1972882Svi117747 (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime(); 1982882Svi117747 #endif 1992882Svi117747 if (future_time <= 0L) { 2002882Svi117747 free(new_timeout); 2012882Svi117747 return (0); 2022882Svi117747 } 2032882Svi117747 2042882Svi117747 new_timeout->sip_timeout_next = NULL; 2052882Svi117747 new_timeout->sip_timeout_val = future_time; 2062882Svi117747 new_timeout->sip_timeout_callback_func = callback_func; 2072882Svi117747 new_timeout->sip_timeout_callback_func_arg = arg; 2082882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 2092882Svi117747 timer_id++; 2102882Svi117747 if (timer_id == 0) 2112882Svi117747 timer_id++; 2122882Svi117747 tid = timer_id; 2132882Svi117747 new_timeout->sip_timeout_id = tid; 2142882Svi117747 last = current = timeout_list; 2152882Svi117747 while (current != NULL) { 2162882Svi117747 if (current->sip_timeout_val <= new_timeout->sip_timeout_val) { 2172882Svi117747 last = current; 2182882Svi117747 current = current->sip_timeout_next; 2192882Svi117747 } else { 2202882Svi117747 break; 2212882Svi117747 } 2222882Svi117747 } 2232882Svi117747 2242882Svi117747 if (current == timeout_list) { 2252882Svi117747 new_timeout->sip_timeout_next = timeout_list; 2262882Svi117747 timeout_list = new_timeout; 2272882Svi117747 } else { 2282882Svi117747 new_timeout->sip_timeout_next = current, 2292882Svi117747 last->sip_timeout_next = new_timeout; 2302882Svi117747 } 2312882Svi117747 (void) pthread_cond_signal(&timeout_cond_var); 2322882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 2332882Svi117747 return (tid); 2342882Svi117747 } 2352882Svi117747 2362882Svi117747 /* 2372882Svi117747 * Schedule the next timeout 2382882Svi117747 */ 2392882Svi117747 static hrtime_t 2402882Svi117747 sip_schedule_to_functions() 2412882Svi117747 { 2422882Svi117747 sip_timeout_t *timeout = NULL; 2432882Svi117747 sip_timeout_t *last = NULL; 2442882Svi117747 boolean_t create_thread = B_FALSE; 2452882Svi117747 hrtime_t current_time; 2462882Svi117747 #ifdef __linux__ 2472882Svi117747 struct timespec tspec; 2482882Svi117747 #endif 2492882Svi117747 2502882Svi117747 /* 2512882Svi117747 * Thread is holding the mutex. 2522882Svi117747 */ 2532882Svi117747 #ifdef __linux__ 2542882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 2552882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 2562882Svi117747 current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + 2572882Svi117747 tspec.tv_nsec; 2582882Svi117747 #else 2592882Svi117747 current_time = gethrtime(); 2602882Svi117747 #endif 2612882Svi117747 if (timeout_list == NULL) 2622882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 2632882Svi117747 timeout = timeout_list; 2642882Svi117747 2652882Svi117747 /* 2662882Svi117747 * Get all the timeouts that have fired. 2672882Svi117747 */ 2682882Svi117747 while (timeout != NULL && timeout->sip_timeout_val <= current_time) { 2692882Svi117747 last = timeout; 2702882Svi117747 timeout = timeout->sip_timeout_next; 2712882Svi117747 } 2722882Svi117747 2732882Svi117747 timeout = last; 2742882Svi117747 if (timeout != NULL) { 2752882Svi117747 if (timeout_current_end != NULL) { 2762882Svi117747 timeout_current_end->sip_timeout_next = timeout_list; 2772882Svi117747 timeout_current_end = timeout; 2782882Svi117747 } else { 2792882Svi117747 timeout_current_start = timeout_list; 2802882Svi117747 timeout_current_end = timeout; 2812882Svi117747 create_thread = B_TRUE; 2822882Svi117747 } 2832882Svi117747 timeout_list = timeout->sip_timeout_next; 2842882Svi117747 timeout->sip_timeout_next = NULL; 2852882Svi117747 if (create_thread) { 2862882Svi117747 pthread_t thr; 2872882Svi117747 2882882Svi117747 (void) pthread_create(&thr, NULL, sip_run_to_functions, 2892882Svi117747 NULL); 2902882Svi117747 (void) pthread_detach(thr); 2912882Svi117747 } 2922882Svi117747 } 2932882Svi117747 if (timeout_list != NULL) 2942882Svi117747 return (timeout_list->sip_timeout_val); 2952882Svi117747 else 2962882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 2972882Svi117747 } 2982882Svi117747 2992882Svi117747 /* 3002882Svi117747 * The timer routine 3012882Svi117747 */ 3022882Svi117747 /* ARGSUSED */ 3032882Svi117747 static void * 3042882Svi117747 sip_timer_thr(void *arg) 3052882Svi117747 { 3062882Svi117747 timestruc_t to; 3072882Svi117747 hrtime_t current_time; 3082882Svi117747 hrtime_t next_timeout; 3092882Svi117747 hrtime_t delta; 310*3439Svi117747 struct timeval tim; 3112882Svi117747 #ifdef __linux__ 3122882Svi117747 struct timespec tspec; 3132882Svi117747 #endif 314*3439Svi117747 delta = (hrtime_t)5 * NANOSEC; 3152882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 3162882Svi117747 for (;;) { 317*3439Svi117747 (void) gettimeofday(&tim, NULL); 318*3439Svi117747 to.tv_sec = tim.tv_sec + (delta / NANOSEC); 319*3439Svi117747 to.tv_nsec = (hrtime_t)(tim.tv_usec * MILLISEC) + 320*3439Svi117747 (delta % NANOSEC); 321*3439Svi117747 if (to.tv_nsec > NANOSEC) { 322*3439Svi117747 to.tv_sec += (to.tv_nsec / NANOSEC); 323*3439Svi117747 to.tv_nsec %= NANOSEC; 324*3439Svi117747 } 3252882Svi117747 (void) pthread_cond_timedwait(&timeout_cond_var, 3262882Svi117747 &timeout_mutex, &to); 3272882Svi117747 /* 3282882Svi117747 * We return from timedwait because we either timed out 3292882Svi117747 * or a new element was added and we need to reset the time 3302882Svi117747 */ 3312882Svi117747 again: 3322882Svi117747 next_timeout = sip_schedule_to_functions(); 3332882Svi117747 #ifdef __linux__ 3342882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 3352882Svi117747 goto again; /* ??? */ 3362882Svi117747 current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + 3372882Svi117747 tspec.tv_nsec; 3382882Svi117747 #else 3392882Svi117747 current_time = gethrtime(); 3402882Svi117747 #endif 3412882Svi117747 delta = next_timeout - current_time; 3422882Svi117747 if (delta <= 0) 3432882Svi117747 goto again; 3442882Svi117747 } 3452882Svi117747 /* NOTREACHED */ 3462882Svi117747 return ((void *)0); 3472882Svi117747 } 3482882Svi117747 3492882Svi117747 /* 3502882Svi117747 * The init routine, starts the timer thread 3512882Svi117747 */ 3522882Svi117747 void 3532882Svi117747 sip_timeout_init() 3542882Svi117747 { 3552882Svi117747 static boolean_t timout_init = B_FALSE; 3562882Svi117747 pthread_t thread1; 3572882Svi117747 3582882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 3592882Svi117747 if (timout_init == B_FALSE) { 3602882Svi117747 timout_init = B_TRUE; 3612882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 3622882Svi117747 } else { 3632882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 3642882Svi117747 return; 3652882Svi117747 } 3662882Svi117747 (void) pthread_create(&thread1, NULL, sip_timer_thr, NULL); 3672882Svi117747 } 368