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