root/timers.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. hash
  2. l_add
  3. l_remove
  4. l_resort
  5. tmr_init
  6. tmr_create
  7. tmr_timeout
  8. tmr_mstimeout
  9. tmr_run
  10. tmr_reset
  11. tmr_cancel
  12. tmr_cleanup
  13. tmr_term
  14. tmr_logstats
  15. 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 }

/* [previous][next][first][last][top][bottom][index][help] */