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