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