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