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