1 /* 2 * Copyright (c) 2009-2011, The Regents of the University of California, 3 * through Lawrence Berkeley National Laboratory (subject to receipt of any 4 * required approvals from the U.S. Dept. of Energy). All rights reserved. 5 * 6 * This code is distributed under a BSD style license, see the LICENSE file 7 * for complete information. 8 * 9 * Based on timers.c by Jef Poskanzer. Used with permission. 10 */ 11 12 #include <sys/types.h> 13 #include <stdlib.h> 14 15 #include "timer.h" 16 17 18 static Timer* timers = NULL; 19 static Timer* free_timers = NULL; 20 21 TimerClientData JunkClientData; 22 23 24 25 /* This is an efficiency tweak. All the routines that need to know the 26 ** current time get passed a pointer to a struct timeval. If it's non-NULL 27 ** it gets used, otherwise we do our own gettimeofday() to fill it in. 28 ** This lets the caller avoid extraneous gettimeofday()s when efficiency 29 ** is needed, and not bother with the extra code when efficiency doesn't 30 ** matter too much. 31 */ 32 static void 33 getnow( struct timeval* nowP, struct timeval* nowP2 ) 34 { 35 if ( nowP != NULL ) 36 *nowP2 = *nowP; 37 else 38 (void) gettimeofday( nowP2, NULL ); 39 } 40 41 42 static void 43 list_add( Timer* t ) 44 { 45 Timer* t2; 46 Timer* t2prev; 47 48 if ( timers == NULL ) { 49 /* The list is empty. */ 50 timers = t; 51 t->prev = t->next = NULL; 52 } else { 53 if ( t->time.tv_sec < timers->time.tv_sec || 54 ( t->time.tv_sec == timers->time.tv_sec && 55 t->time.tv_usec < timers->time.tv_usec ) ) { 56 /* The new timer goes at the head of the list. */ 57 t->prev = NULL; 58 t->next = timers; 59 timers->prev = t; 60 timers = t; 61 } else { 62 /* Walk the list to find the insertion point. */ 63 for ( t2prev = timers, t2 = timers->next; t2 != NULL; 64 t2prev = t2, t2 = t2->next ) { 65 if ( t->time.tv_sec < t2->time.tv_sec || 66 ( t->time.tv_sec == t2->time.tv_sec && 67 t->time.tv_usec < t2->time.tv_usec ) ) { 68 /* Found it. */ 69 t2prev->next = t; 70 t->prev = t2prev; 71 t->next = t2; 72 t2->prev = t; 73 return; 74 } 75 } 76 /* Oops, got to the end of the list. Add to tail. */ 77 t2prev->next = t; 78 t->prev = t2prev; 79 t->next = NULL; 80 } 81 } 82 } 83 84 85 static void 86 list_remove( Timer* t ) 87 { 88 if ( t->prev == NULL ) 89 timers = t->next; 90 else 91 t->prev->next = t->next; 92 if ( t->next != NULL ) 93 t->next->prev = t->prev; 94 } 95 96 97 static void 98 list_resort( Timer* t ) 99 { 100 /* Remove the timer from the list. */ 101 list_remove( t ); 102 /* And add it back in, sorted correctly. */ 103 list_add( t ); 104 } 105 106 107 static void 108 add_usecs( struct timeval* t, int64_t usecs ) 109 { 110 t->tv_sec += usecs / 1000000L; 111 t->tv_usec += usecs % 1000000L; 112 if ( t->tv_usec >= 1000000L ) { 113 t->tv_sec += t->tv_usec / 1000000L; 114 t->tv_usec %= 1000000L; 115 } 116 } 117 118 119 Timer* 120 tmr_create( 121 struct timeval* nowP, TimerProc* timer_proc, TimerClientData client_data, 122 int64_t usecs, int periodic ) 123 { 124 struct timeval now; 125 Timer* t; 126 127 getnow( nowP, &now ); 128 129 if ( free_timers != NULL ) { 130 t = free_timers; 131 free_timers = t->next; 132 } else { 133 t = (Timer*) malloc( sizeof(Timer) ); 134 if ( t == NULL ) 135 return NULL; 136 } 137 138 t->timer_proc = timer_proc; 139 t->client_data = client_data; 140 t->usecs = usecs; 141 t->periodic = periodic; 142 t->time = now; 143 add_usecs( &t->time, usecs ); 144 /* Add the new timer to the active list. */ 145 list_add( t ); 146 147 return t; 148 } 149 150 151 struct timeval* 152 tmr_timeout( struct timeval* nowP ) 153 { 154 struct timeval now; 155 int64_t usecs; 156 static struct timeval timeout; 157 158 getnow( nowP, &now ); 159 /* Since the list is sorted, we only need to look at the first timer. */ 160 if ( timers == NULL ) 161 return NULL; 162 usecs = ( timers->time.tv_sec - now.tv_sec ) * 1000000LL + 163 ( timers->time.tv_usec - now.tv_usec ); 164 if ( usecs <= 0 ) 165 usecs = 0; 166 timeout.tv_sec = usecs / 1000000LL; 167 timeout.tv_usec = usecs % 1000000LL; 168 return &timeout; 169 } 170 171 172 void 173 tmr_run( struct timeval* nowP ) 174 { 175 struct timeval now; 176 Timer* t; 177 Timer* next; 178 179 getnow( nowP, &now ); 180 for ( t = timers; t != NULL; t = next ) { 181 next = t->next; 182 /* Since the list is sorted, as soon as we find a timer 183 ** that isn't ready yet, we are done. 184 */ 185 if ( t->time.tv_sec > now.tv_sec || 186 ( t->time.tv_sec == now.tv_sec && 187 t->time.tv_usec > now.tv_usec ) ) 188 break; 189 (t->timer_proc)( t->client_data, &now ); 190 if ( t->periodic ) { 191 /* Reschedule. */ 192 add_usecs( &t->time, t->usecs ); 193 list_resort( t ); 194 } else 195 tmr_cancel( t ); 196 } 197 } 198 199 200 void 201 tmr_reset( struct timeval* nowP, Timer* t ) 202 { 203 struct timeval now; 204 205 getnow( nowP, &now ); 206 t->time = now; 207 add_usecs( &t->time, t->usecs ); 208 list_resort( t ); 209 } 210 211 212 void 213 tmr_cancel( Timer* t ) 214 { 215 /* Remove it from the active list. */ 216 list_remove( t ); 217 /* And put it on the free list. */ 218 t->next = free_timers; 219 free_timers = t; 220 t->prev = NULL; 221 } 222 223 224 void 225 tmr_cleanup( void ) 226 { 227 Timer* t; 228 229 while ( free_timers != NULL ) { 230 t = free_timers; 231 free_timers = t->next; 232 free( (void*) t ); 233 } 234 } 235 236 237 void 238 tmr_destroy( void ) 239 { 240 while ( timers != NULL ) 241 tmr_cancel( timers ); 242 tmr_cleanup(); 243 } 244