1*2882Svi117747 /* 2*2882Svi117747 * CDDL HEADER START 3*2882Svi117747 * 4*2882Svi117747 * The contents of this file are subject to the terms of the 5*2882Svi117747 * Common Development and Distribution License (the "License"). 6*2882Svi117747 * You may not use this file except in compliance with the License. 7*2882Svi117747 * 8*2882Svi117747 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*2882Svi117747 * or http://www.opensolaris.org/os/licensing. 10*2882Svi117747 * See the License for the specific language governing permissions 11*2882Svi117747 * and limitations under the License. 12*2882Svi117747 * 13*2882Svi117747 * When distributing Covered Code, include this CDDL HEADER in each 14*2882Svi117747 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*2882Svi117747 * If applicable, add the following below this CDDL HEADER, with the 16*2882Svi117747 * fields enclosed by brackets "[]" replaced with your own identifying 17*2882Svi117747 * information: Portions Copyright [yyyy] [name of copyright owner] 18*2882Svi117747 * 19*2882Svi117747 * CDDL HEADER END 20*2882Svi117747 */ 21*2882Svi117747 22*2882Svi117747 /* 23*2882Svi117747 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 24*2882Svi117747 * Use is subject to license terms. 25*2882Svi117747 */ 26*2882Svi117747 27*2882Svi117747 #pragma ident "%Z%%M% %I% %E% SMI" 28*2882Svi117747 29*2882Svi117747 /* 30*2882Svi117747 * Simple implementation of timeout functionality. The granuality is a sec 31*2882Svi117747 */ 32*2882Svi117747 #include <stdio.h> 33*2882Svi117747 #include <pthread.h> 34*2882Svi117747 #include <sys/errno.h> 35*2882Svi117747 #include <stdlib.h> 36*2882Svi117747 37*2882Svi117747 #include "sip_miscdefs.h" 38*2882Svi117747 39*2882Svi117747 uint_t sip_timeout(void *arg, void (*callback_func)(void *), 40*2882Svi117747 struct timeval *timeout_time); 41*2882Svi117747 boolean_t sip_untimeout(uint_t); 42*2882Svi117747 43*2882Svi117747 typedef struct timeout { 44*2882Svi117747 struct timeout *sip_timeout_next; 45*2882Svi117747 hrtime_t sip_timeout_val; 46*2882Svi117747 void (*sip_timeout_callback_func)(void *); 47*2882Svi117747 void *sip_timeout_callback_func_arg; 48*2882Svi117747 int sip_timeout_id; 49*2882Svi117747 } sip_timeout_t; 50*2882Svi117747 51*2882Svi117747 static pthread_mutex_t timeout_mutex = PTHREAD_MUTEX_INITIALIZER; 52*2882Svi117747 static pthread_cond_t timeout_cond_var = PTHREAD_COND_INITIALIZER; 53*2882Svi117747 static sip_timeout_t *timeout_list; 54*2882Svi117747 static sip_timeout_t *timeout_current_start; 55*2882Svi117747 static sip_timeout_t *timeout_current_end; 56*2882Svi117747 57*2882Svi117747 /* 58*2882Svi117747 * LONG_SLEEP_TIME = (24 * 60 * 60 * NANOSEC) 59*2882Svi117747 */ 60*2882Svi117747 #define LONG_SLEEP_TIME (0x15180LL * 0x3B9ACA00LL) 61*2882Svi117747 62*2882Svi117747 uint_t timer_id = 0; 63*2882Svi117747 64*2882Svi117747 /* 65*2882Svi117747 * Invoke the callback function 66*2882Svi117747 */ 67*2882Svi117747 /* ARGSUSED */ 68*2882Svi117747 static void * 69*2882Svi117747 sip_run_to_functions(void *arg) 70*2882Svi117747 { 71*2882Svi117747 sip_timeout_t *timeout = NULL; 72*2882Svi117747 73*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 74*2882Svi117747 while (timeout_current_start != NULL) { 75*2882Svi117747 timeout = timeout_current_start; 76*2882Svi117747 if (timeout_current_end == timeout_current_start) 77*2882Svi117747 timeout_current_start = timeout_current_end = NULL; 78*2882Svi117747 else 79*2882Svi117747 timeout_current_start = timeout->sip_timeout_next; 80*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 81*2882Svi117747 timeout->sip_timeout_callback_func( 82*2882Svi117747 timeout->sip_timeout_callback_func_arg); 83*2882Svi117747 free(timeout); 84*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 85*2882Svi117747 } 86*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 87*2882Svi117747 pthread_exit(NULL); 88*2882Svi117747 return ((void *)0); 89*2882Svi117747 } 90*2882Svi117747 91*2882Svi117747 /* 92*2882Svi117747 * In the very very unlikely case timer id wraps around and we have two timers 93*2882Svi117747 * with the same id. If that happens timer with the least amount of time left 94*2882Svi117747 * will be deleted. In case both timers have same time left than the one that 95*2882Svi117747 * was scheduled first will be deleted as it will be in the front of the list. 96*2882Svi117747 */ 97*2882Svi117747 boolean_t 98*2882Svi117747 sip_untimeout(uint_t id) 99*2882Svi117747 { 100*2882Svi117747 boolean_t ret = B_FALSE; 101*2882Svi117747 sip_timeout_t *current, *last; 102*2882Svi117747 103*2882Svi117747 last = NULL; 104*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 105*2882Svi117747 106*2882Svi117747 /* 107*2882Svi117747 * Check if this is in the to-be run list 108*2882Svi117747 */ 109*2882Svi117747 if (timeout_current_start != NULL) { 110*2882Svi117747 current = timeout_current_start; 111*2882Svi117747 while (current != NULL) { 112*2882Svi117747 if (current->sip_timeout_id == id) { 113*2882Svi117747 if (current == timeout_current_start) { 114*2882Svi117747 timeout_current_start = 115*2882Svi117747 current->sip_timeout_next; 116*2882Svi117747 } else { 117*2882Svi117747 last->sip_timeout_next = 118*2882Svi117747 current->sip_timeout_next; 119*2882Svi117747 } 120*2882Svi117747 if (current == timeout_current_end) 121*2882Svi117747 timeout_current_end = last; 122*2882Svi117747 if (current->sip_timeout_callback_func_arg != 123*2882Svi117747 NULL) { 124*2882Svi117747 free(current-> 125*2882Svi117747 sip_timeout_callback_func_arg); 126*2882Svi117747 current->sip_timeout_callback_func_arg = 127*2882Svi117747 NULL; 128*2882Svi117747 } 129*2882Svi117747 free(current); 130*2882Svi117747 ret = B_TRUE; 131*2882Svi117747 break; 132*2882Svi117747 } 133*2882Svi117747 last = current; 134*2882Svi117747 current = current->sip_timeout_next; 135*2882Svi117747 } 136*2882Svi117747 } 137*2882Svi117747 138*2882Svi117747 /* 139*2882Svi117747 * Check if this is in the to-be scheduled list 140*2882Svi117747 */ 141*2882Svi117747 if (!ret && timeout_list != NULL) { 142*2882Svi117747 last = NULL; 143*2882Svi117747 current = timeout_list; 144*2882Svi117747 while (current != NULL) { 145*2882Svi117747 if (current->sip_timeout_id == id) { 146*2882Svi117747 if (current == timeout_list) { 147*2882Svi117747 timeout_list = 148*2882Svi117747 current->sip_timeout_next; 149*2882Svi117747 } else { 150*2882Svi117747 last->sip_timeout_next = 151*2882Svi117747 current->sip_timeout_next; 152*2882Svi117747 } 153*2882Svi117747 if (current->sip_timeout_callback_func_arg != 154*2882Svi117747 NULL) { 155*2882Svi117747 free(current-> 156*2882Svi117747 sip_timeout_callback_func_arg); 157*2882Svi117747 current->sip_timeout_callback_func_arg = 158*2882Svi117747 NULL; 159*2882Svi117747 } 160*2882Svi117747 free(current); 161*2882Svi117747 ret = B_TRUE; 162*2882Svi117747 break; 163*2882Svi117747 } 164*2882Svi117747 last = current; 165*2882Svi117747 current = current->sip_timeout_next; 166*2882Svi117747 } 167*2882Svi117747 } 168*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 169*2882Svi117747 return (ret); 170*2882Svi117747 } 171*2882Svi117747 172*2882Svi117747 /* 173*2882Svi117747 * Add a new timeout 174*2882Svi117747 */ 175*2882Svi117747 uint_t 176*2882Svi117747 sip_timeout(void *arg, void (*callback_func)(void *), 177*2882Svi117747 struct timeval *timeout_time) 178*2882Svi117747 { 179*2882Svi117747 sip_timeout_t *new_timeout; 180*2882Svi117747 sip_timeout_t *current, *last; 181*2882Svi117747 hrtime_t future_time; 182*2882Svi117747 uint_t tid; 183*2882Svi117747 #ifdef __linux__ 184*2882Svi117747 struct timespec tspec; 185*2882Svi117747 hrtime_t now; 186*2882Svi117747 #endif 187*2882Svi117747 188*2882Svi117747 new_timeout = malloc(sizeof (sip_timeout_t)); 189*2882Svi117747 if (new_timeout == NULL) 190*2882Svi117747 return (0); 191*2882Svi117747 192*2882Svi117747 #ifdef __linux__ 193*2882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 194*2882Svi117747 return (0); 195*2882Svi117747 now = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + tspec.tv_nsec; 196*2882Svi117747 future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC + 197*2882Svi117747 (hrtime_t)(timeout_time->tv_usec * MILLISEC) + now; 198*2882Svi117747 #else 199*2882Svi117747 future_time = (hrtime_t)timeout_time->tv_sec * (hrtime_t)NANOSEC + 200*2882Svi117747 (hrtime_t)(timeout_time->tv_usec * MILLISEC) + gethrtime(); 201*2882Svi117747 #endif 202*2882Svi117747 if (future_time <= 0L) { 203*2882Svi117747 free(new_timeout); 204*2882Svi117747 return (0); 205*2882Svi117747 } 206*2882Svi117747 207*2882Svi117747 new_timeout->sip_timeout_next = NULL; 208*2882Svi117747 new_timeout->sip_timeout_val = future_time; 209*2882Svi117747 new_timeout->sip_timeout_callback_func = callback_func; 210*2882Svi117747 new_timeout->sip_timeout_callback_func_arg = arg; 211*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 212*2882Svi117747 timer_id++; 213*2882Svi117747 if (timer_id == 0) 214*2882Svi117747 timer_id++; 215*2882Svi117747 tid = timer_id; 216*2882Svi117747 new_timeout->sip_timeout_id = tid; 217*2882Svi117747 last = current = timeout_list; 218*2882Svi117747 while (current != NULL) { 219*2882Svi117747 if (current->sip_timeout_val <= new_timeout->sip_timeout_val) { 220*2882Svi117747 last = current; 221*2882Svi117747 current = current->sip_timeout_next; 222*2882Svi117747 } else { 223*2882Svi117747 break; 224*2882Svi117747 } 225*2882Svi117747 } 226*2882Svi117747 227*2882Svi117747 if (current == timeout_list) { 228*2882Svi117747 new_timeout->sip_timeout_next = timeout_list; 229*2882Svi117747 timeout_list = new_timeout; 230*2882Svi117747 } else { 231*2882Svi117747 new_timeout->sip_timeout_next = current, 232*2882Svi117747 last->sip_timeout_next = new_timeout; 233*2882Svi117747 } 234*2882Svi117747 (void) pthread_cond_signal(&timeout_cond_var); 235*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 236*2882Svi117747 return (tid); 237*2882Svi117747 } 238*2882Svi117747 239*2882Svi117747 /* 240*2882Svi117747 * Schedule the next timeout 241*2882Svi117747 */ 242*2882Svi117747 static hrtime_t 243*2882Svi117747 sip_schedule_to_functions() 244*2882Svi117747 { 245*2882Svi117747 sip_timeout_t *timeout = NULL; 246*2882Svi117747 sip_timeout_t *last = NULL; 247*2882Svi117747 boolean_t create_thread = B_FALSE; 248*2882Svi117747 hrtime_t current_time; 249*2882Svi117747 #ifdef __linux__ 250*2882Svi117747 struct timespec tspec; 251*2882Svi117747 #endif 252*2882Svi117747 253*2882Svi117747 /* 254*2882Svi117747 * Thread is holding the mutex. 255*2882Svi117747 */ 256*2882Svi117747 #ifdef __linux__ 257*2882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 258*2882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 259*2882Svi117747 current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + 260*2882Svi117747 tspec.tv_nsec; 261*2882Svi117747 #else 262*2882Svi117747 current_time = gethrtime(); 263*2882Svi117747 #endif 264*2882Svi117747 if (timeout_list == NULL) 265*2882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 266*2882Svi117747 timeout = timeout_list; 267*2882Svi117747 268*2882Svi117747 /* 269*2882Svi117747 * Get all the timeouts that have fired. 270*2882Svi117747 */ 271*2882Svi117747 while (timeout != NULL && timeout->sip_timeout_val <= current_time) { 272*2882Svi117747 last = timeout; 273*2882Svi117747 timeout = timeout->sip_timeout_next; 274*2882Svi117747 } 275*2882Svi117747 276*2882Svi117747 timeout = last; 277*2882Svi117747 if (timeout != NULL) { 278*2882Svi117747 if (timeout_current_end != NULL) { 279*2882Svi117747 timeout_current_end->sip_timeout_next = timeout_list; 280*2882Svi117747 timeout_current_end = timeout; 281*2882Svi117747 } else { 282*2882Svi117747 timeout_current_start = timeout_list; 283*2882Svi117747 timeout_current_end = timeout; 284*2882Svi117747 create_thread = B_TRUE; 285*2882Svi117747 } 286*2882Svi117747 timeout_list = timeout->sip_timeout_next; 287*2882Svi117747 timeout->sip_timeout_next = NULL; 288*2882Svi117747 if (create_thread) { 289*2882Svi117747 pthread_t thr; 290*2882Svi117747 291*2882Svi117747 (void) pthread_create(&thr, NULL, sip_run_to_functions, 292*2882Svi117747 NULL); 293*2882Svi117747 (void) pthread_detach(thr); 294*2882Svi117747 } 295*2882Svi117747 } 296*2882Svi117747 if (timeout_list != NULL) 297*2882Svi117747 return (timeout_list->sip_timeout_val); 298*2882Svi117747 else 299*2882Svi117747 return ((hrtime_t)LONG_SLEEP_TIME + current_time); 300*2882Svi117747 } 301*2882Svi117747 302*2882Svi117747 /* 303*2882Svi117747 * The timer routine 304*2882Svi117747 */ 305*2882Svi117747 /* ARGSUSED */ 306*2882Svi117747 static void * 307*2882Svi117747 sip_timer_thr(void *arg) 308*2882Svi117747 { 309*2882Svi117747 timestruc_t to; 310*2882Svi117747 hrtime_t current_time; 311*2882Svi117747 hrtime_t next_timeout; 312*2882Svi117747 hrtime_t delta; 313*2882Svi117747 #ifdef __linux__ 314*2882Svi117747 struct timespec tspec; 315*2882Svi117747 #endif 316*2882Svi117747 to.tv_sec = time(NULL) + 5; 317*2882Svi117747 to.tv_nsec = 0; 318*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 319*2882Svi117747 for (;;) { 320*2882Svi117747 (void) pthread_cond_timedwait(&timeout_cond_var, 321*2882Svi117747 &timeout_mutex, &to); 322*2882Svi117747 /* 323*2882Svi117747 * We return from timedwait because we either timed out 324*2882Svi117747 * or a new element was added and we need to reset the time 325*2882Svi117747 */ 326*2882Svi117747 again: 327*2882Svi117747 next_timeout = sip_schedule_to_functions(); 328*2882Svi117747 #ifdef __linux__ 329*2882Svi117747 if (clock_gettime(CLOCK_REALTIME, &tspec) != 0) 330*2882Svi117747 goto again; /* ??? */ 331*2882Svi117747 current_time = (hrtime_t)tspec.tv_sec * (hrtime_t)NANOSEC + 332*2882Svi117747 tspec.tv_nsec; 333*2882Svi117747 #else 334*2882Svi117747 current_time = gethrtime(); 335*2882Svi117747 #endif 336*2882Svi117747 delta = next_timeout - current_time; 337*2882Svi117747 if (delta <= 0) 338*2882Svi117747 goto again; 339*2882Svi117747 to.tv_sec = time(NULL) + (delta / NANOSEC); 340*2882Svi117747 to.tv_nsec = delta % NANOSEC; 341*2882Svi117747 } 342*2882Svi117747 /* NOTREACHED */ 343*2882Svi117747 return ((void *)0); 344*2882Svi117747 } 345*2882Svi117747 346*2882Svi117747 /* 347*2882Svi117747 * The init routine, starts the timer thread 348*2882Svi117747 */ 349*2882Svi117747 void 350*2882Svi117747 sip_timeout_init() 351*2882Svi117747 { 352*2882Svi117747 static boolean_t timout_init = B_FALSE; 353*2882Svi117747 pthread_t thread1; 354*2882Svi117747 355*2882Svi117747 (void) pthread_mutex_lock(&timeout_mutex); 356*2882Svi117747 if (timout_init == B_FALSE) { 357*2882Svi117747 timout_init = B_TRUE; 358*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 359*2882Svi117747 } else { 360*2882Svi117747 (void) pthread_mutex_unlock(&timeout_mutex); 361*2882Svi117747 return; 362*2882Svi117747 } 363*2882Svi117747 (void) pthread_create(&thread1, NULL, sip_timer_thr, NULL); 364*2882Svi117747 } 365