root/timers.c
/*DEFINITIONS
This source file includes following definitions.- hash
- l_add
- l_remove
- l_resort
- tmr_init
- tmr_create
- tmr_timeout
- tmr_mstimeout
- tmr_run
- tmr_reset
- tmr_cancel
- tmr_cleanup
- tmr_term
- tmr_logstats
- tmr_prepare_timeval
1 /* timers.c - simple timer routines 2 ** 3 ** Copyright (c) 1995,1998,2000,2014 by Jef Poskanzer <jef@mail.acme.com>. 4 ** Copyright (c) 2023 by Amelia Zabardast Ziabari <ame@psianesia.org>. 5 ** All rights reserved. 6 ** 7 ** Redistribution and use in source and binary forms, with or without 8 ** modification, are permitted provided that the following conditions 9 ** are met: 10 ** 1. Redistributions of source code must retain the above copyright 11 ** notice, this list of conditions and the following disclaimer. 12 ** 2. Redistributions in binary form must reproduce the above copyright 13 ** notice, this list of conditions and the following disclaimer in the 14 ** documentation and/or other materials provided with the distribution. 15 ** 16 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 ** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 ** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 ** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 ** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 ** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 ** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 ** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 ** SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 31 #include <stdlib.h> 32 #include <stdio.h> 33 #include <syslog.h> 34 35 #include "timers.h" 36 37 38 #define HASH_SIZE 67 39 static Timer* timers[HASH_SIZE]; 40 static Timer* free_timers; 41 static int alloc_count, active_count, free_count; 42 43 ClientData JunkClientData; 44 45 #undef HAVE_CLOCK_MONO 46 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) 47 #define HAVE_CLOCK_MONO 48 static int use_monotonic = 0; /* monotonic clock runtime availability flag */ 49 static struct timeval tv_diff; /* system time - monotonic diff. at start */ 50 #endif 51 52 static unsigned int 53 hash( Timer* t ) 54 { 55 /* We can hash on the trigger time, even though it can change over 56 ** the life of a timer via either the periodic bit or the tmr_reset() 57 ** call. This is because both of those guys call l_resort(), which 58 ** recomputes the hash and moves the timer to the appropriate list. 59 */ 60 return ( 61 (unsigned int) t->time.tv_sec ^ 62 (unsigned int) t->time.tv_usec ) % HASH_SIZE; 63 } 64 65 66 static void 67 l_add( Timer* t ) 68 { 69 int h = t->hash; 70 Timer* t2; 71 Timer* t2prev; 72 73 t2 = timers[h]; 74 if ( t2 == (Timer*) 0 ) 75 { 76 /* The list is empty. */ 77 timers[h] = t; 78 t->prev = t->next = (Timer*) 0; 79 } 80 else 81 { 82 if ( t->time.tv_sec < t2->time.tv_sec || 83 ( t->time.tv_sec == t2->time.tv_sec && 84 t->time.tv_usec <= t2->time.tv_usec ) ) 85 { 86 /* The new timer goes at the head of the list. */ 87 timers[h] = t; 88 t->prev = (Timer*) 0; 89 t->next = t2; 90 t2->prev = t; 91 } 92 else 93 { 94 /* Walk the list to find the insertion point. */ 95 for ( t2prev = t2, t2 = t2->next; t2 != (Timer*) 0; 96 t2prev = t2, t2 = t2->next ) 97 { 98 if ( t->time.tv_sec < t2->time.tv_sec || 99 ( t->time.tv_sec == t2->time.tv_sec && 100 t->time.tv_usec <= t2->time.tv_usec ) ) 101 { 102 /* Found it. */ 103 t2prev->next = t; 104 t->prev = t2prev; 105 t->next = t2; 106 t2->prev = t; 107 return; 108 } 109 } 110 /* Oops, got to the end of the list. Add to tail. */ 111 t2prev->next = t; 112 t->prev = t2prev; 113 t->next = (Timer*) 0; 114 } 115 } 116 } 117 118 119 static void 120 l_remove( Timer* t ) 121 { 122 int h = t->hash; 123 124 if ( t->prev == (Timer*) 0 ) 125 timers[h] = t->next; 126 else 127 t->prev->next = t->next; 128 if ( t->next != (Timer*) 0 ) 129 t->next->prev = t->prev; 130 } 131 132 133 static void 134 l_resort( Timer* t ) 135 { 136 /* Remove the timer from its old list. */ 137 l_remove( t ); 138 /* Recompute the hash. */ 139 t->hash = hash( t ); 140 /* And add it back in to its new list, sorted correctly. */ 141 l_add( t ); 142 } 143 144 145 void 146 tmr_init( void ) 147 { 148 int h; 149 150 for ( h = 0; h < HASH_SIZE; ++h ) 151 timers[h] = (Timer*) 0; 152 free_timers = (Timer*) 0; 153 alloc_count = active_count = free_count = 0; 154 155 /* Check for monotonic clock availability */ 156 #ifdef HAVE_CLOCK_MONO 157 struct timespec ts; 158 struct timeval tv_start, tv; 159 160 /* Try to get monotonic clock time */ 161 if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) { 162 use_monotonic = 1; 163 164 /* Get current system time */ 165 (void) gettimeofday( &tv_start , (struct timezone*) 0 ); 166 tv.tv_sec = ts.tv_sec; 167 tv.tv_usec = ts.tv_nsec / 1000L; 168 /* Calculate and save the difference: tv_start is since the Epoch, so 169 tv_start > ts 170 tv_diff = tv_start - tv */ 171 timersub( &tv_start, &tv, &tv_diff ); 172 } 173 #endif 174 175 } 176 177 178 Timer* 179 tmr_create( 180 struct timeval* nowP, TimerProc* timer_proc, ClientData client_data, 181 long msecs, int periodic ) 182 { 183 Timer* t; 184 185 if ( free_timers != (Timer*) 0 ) 186 { 187 t = free_timers; 188 free_timers = t->next; 189 --free_count; 190 } 191 else 192 { 193 t = (Timer*) malloc( sizeof(Timer) ); 194 if ( t == (Timer*) 0 ) 195 return (Timer*) 0; 196 ++alloc_count; 197 } 198 199 t->timer_proc = timer_proc; 200 t->client_data = client_data; 201 t->msecs = msecs; 202 t->periodic = periodic; 203 if ( nowP != (struct timeval*) 0 ) 204 t->time = *nowP; 205 else 206 tmr_prepare_timeval( &t->time ); 207 t->time.tv_sec += msecs / 1000L; 208 t->time.tv_usec += ( msecs % 1000L ) * 1000L; 209 if ( t->time.tv_usec >= 1000000L ) 210 { 211 t->time.tv_sec += t->time.tv_usec / 1000000L; 212 t->time.tv_usec %= 1000000L; 213 } 214 t->hash = hash( t ); 215 /* Add the new timer to the proper active list. */ 216 l_add( t ); 217 ++active_count; 218 219 return t; 220 } 221 222 223 struct timeval* 224 tmr_timeout( struct timeval* nowP ) 225 { 226 long msecs; 227 static struct timeval timeout; 228 229 msecs = tmr_mstimeout( nowP ); 230 if ( msecs == INFTIM ) 231 return (struct timeval*) 0; 232 timeout.tv_sec = msecs / 1000L; 233 timeout.tv_usec = ( msecs % 1000L ) * 1000L; 234 return &timeout; 235 } 236 237 238 long 239 tmr_mstimeout( struct timeval* nowP ) 240 { 241 int h; 242 int gotone; 243 long msecs, m; 244 Timer* t; 245 246 gotone = 0; 247 msecs = 0; /* make lint happy */ 248 /* Since the lists are sorted, we only need to look at the 249 ** first timer on each one. 250 */ 251 for ( h = 0; h < HASH_SIZE; ++h ) 252 { 253 t = timers[h]; 254 if ( t != (Timer*) 0 ) 255 { 256 m = ( t->time.tv_sec - nowP->tv_sec ) * 1000L + 257 ( t->time.tv_usec - nowP->tv_usec ) / 1000L; 258 if ( ! gotone ) 259 { 260 msecs = m; 261 gotone = 1; 262 } 263 else if ( m < msecs ) 264 msecs = m; 265 } 266 } 267 if ( ! gotone ) 268 return INFTIM; 269 if ( msecs <= 0 ) 270 msecs = 500; /* was 0, but we should never poll() < 500 msec */ 271 return msecs; 272 } 273 274 275 void 276 tmr_run( struct timeval* nowP ) 277 { 278 int h; 279 Timer* t; 280 Timer* next; 281 282 for ( h = 0; h < HASH_SIZE; ++h ) 283 for ( t = timers[h]; t != (Timer*) 0; t = next ) 284 { 285 next = t->next; 286 /* Since the lists are sorted, as soon as we find a timer 287 ** that isn't ready yet, we can go on to the next list. 288 */ 289 if ( t->time.tv_sec > nowP->tv_sec || 290 ( t->time.tv_sec == nowP->tv_sec && 291 t->time.tv_usec > nowP->tv_usec ) ) 292 break; 293 (t->timer_proc)( t->client_data, nowP ); 294 if ( t->periodic ) 295 { 296 /* Reschedule. */ 297 t->time.tv_sec += t->msecs / 1000L; 298 t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; 299 if ( t->time.tv_usec >= 1000000L ) 300 { 301 t->time.tv_sec += t->time.tv_usec / 1000000L; 302 t->time.tv_usec %= 1000000L; 303 } 304 l_resort( t ); 305 } 306 else 307 tmr_cancel( t ); 308 } 309 } 310 311 312 void 313 tmr_reset( struct timeval* nowP, Timer* t ) 314 { 315 t->time = *nowP; 316 t->time.tv_sec += t->msecs / 1000L; 317 t->time.tv_usec += ( t->msecs % 1000L ) * 1000L; 318 if ( t->time.tv_usec >= 1000000L ) 319 { 320 t->time.tv_sec += t->time.tv_usec / 1000000L; 321 t->time.tv_usec %= 1000000L; 322 } 323 l_resort( t ); 324 } 325 326 327 void 328 tmr_cancel( Timer* t ) 329 { 330 /* Remove it from its active list. */ 331 l_remove( t ); 332 --active_count; 333 /* And put it on the free list. */ 334 t->next = free_timers; 335 free_timers = t; 336 ++free_count; 337 t->prev = (Timer*) 0; 338 } 339 340 341 void 342 tmr_cleanup( void ) 343 { 344 Timer* t; 345 346 while ( free_timers != (Timer*) 0 ) 347 { 348 t = free_timers; 349 free_timers = t->next; 350 --free_count; 351 free(t); 352 --alloc_count; 353 } 354 } 355 356 357 void 358 tmr_term( void ) 359 { 360 int h; 361 362 for ( h = 0; h < HASH_SIZE; ++h ) 363 while ( timers[h] != (Timer*) 0 ) 364 tmr_cancel( timers[h] ); 365 tmr_cleanup(); 366 } 367 368 369 /* Generate debugging statistics syslog message. */ 370 void 371 tmr_logstats( long secs ) 372 { 373 (void) secs; /* XXX: gcc */ 374 375 syslog( 376 LOG_NOTICE, " timers - %d allocated, %d active, %d free", 377 alloc_count, active_count, free_count ); 378 if ( active_count + free_count != alloc_count ) 379 syslog( LOG_ERR, "timer counts don't add up!" ); 380 } 381 382 /* Fill timeval structure for further usage by the package. */ 383 void 384 tmr_prepare_timeval( struct timeval *tv ) 385 { 386 #ifdef HAVE_CLOCK_MONO 387 struct timespec ts; 388 struct timeval tv0; 389 390 if (use_monotonic) { /* use monotonic clock source ? */ 391 if (clock_gettime(CLOCK_MONOTONIC,&ts) < 0) { 392 perror("clock_gettime"); return; 393 } 394 tv0.tv_sec = ts.tv_sec; 395 tv0.tv_usec = ts.tv_nsec / 1000L; 396 /* Return system time value like it was running accurately */ 397 timeradd( &tv_diff, &tv0, tv ); 398 } else { 399 #endif 400 (void) gettimeofday( tv , (struct timezone*) 0 ); 401 #ifdef HAVE_CLOCK_MONO 402 } 403 #endif 404 }
/*