diff options
Diffstat (limited to 'timer.c')
-rw-r--r-- | timer.c | 472 |
1 files changed, 395 insertions, 77 deletions
@@ -1,9 +1,9 @@ -/* $Id: timer.c 728 2007-01-14 11:14:38Z michael $ - * $URL: https://ssl.bulix.org/svn/lcd4linux/branches/0.10.1/timer.c $ +/* $Id: timer.c 1143 2011-02-12 22:46:19Z mzuther $ + * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/timer.c $ * - * generic timer handling + * Generic timer handling. * - * Copyright (C) 2003, 2004 Michael Reinelt <reinelt@eunet.at> + * Copyright (C) 2003, 2004 Michael Reinelt <michael@reinelt.co.at> * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net> * * This program is free software; you can redistribute it and/or modify @@ -22,17 +22,36 @@ * */ -/* - * exported functions: +/* + * Exported functions: * - * int timer_add (void(*callback)(void *data), void *data, int interval, int one_shot); - * adds a timer to the queue + * int timer_add(void (*callback) (void *data), void *data, const int + * interval, const int one_shot) * - * int timer_process (struct timespec *delay); - * process timer queue + * Create a new timer and add it to the timer queue. * - * void timer_exit(); - * release all timers + * + * int timer_add_late(void (*callback) (void *data), void *data, const + * int interval, const int one_shot) + * + * This function creates a new timer and adds it to the timer queue + * just as timer_add() does, but the timer will NOT be triggered + * immediately (useful for scheduling things). + * + * + * int timer_process(struct timespec *delay) + * + * Process timer queue. + * + * + * int timer_remove(void (*callback) (void *data), void *data) + * + * Remove a new timer with given callback and data. + * + * + * void timer_exit(void) + * + * Release all timers and free the associated memory block. * */ @@ -53,143 +72,442 @@ #include <dmalloc.h> #endif +/* threshold in milliseconds that differentiates between clock skew + and clock jitter */ +#define CLOCK_SKEW_DETECT_TIME_IN_MS 1000 +/* structure for storing all relevant data of a single timer */ typedef struct TIMER { + /* pointer to function of type void func(void *data) that will be + called when the timer is processed; it will also be used to + identify a specific timer */ void (*callback) (void *data); + + /* pointer to data which will be passed to the callback function; + it will also be used to identify a specific timer */ void *data; + + /* struct to hold the time (in seconds and milliseconds since the + Epoch) when the timer will be processed for the next time */ struct timeval when; + + /* specifies the timer's triggering interval in milliseconds */ int interval; + + /* specifies whether the timer should trigger indefinitely until + it is deleted (value of 0) or only once (all other values) */ int one_shot; + + /* marks timer as being active (so it will get processed) or + inactive (which means the timer has been deleted and its + allocated memory may be re-used) */ int active; } TIMER; +/* number of allocated timer slots */ +int nTimers = 0; +/* pointer to memory allocated for storing the timer slots */ TIMER *Timers = NULL; -int nTimers = 0; -static void timer_inc(struct timeval *tv, const int msec) +static void timer_inc(const int timer, struct timeval *now) +/* Update the time a given timer updates next. + + timer (integer): internal ID of timer that is to be updated + + now (timeval pointer): struct holding the "current" time + + return value: void + */ +{ + /* calculate the time difference between the last time the given + timer has been processed and the current time */ + struct timeval diff; + timersub(now, &Timers[timer].when, &diff); + + /* convert this time difference to fractional seconds */ + float time_difference = diff.tv_sec + diff.tv_usec / 1000000.0f; + + /* convert time difference to fractional milliseconds */ + time_difference = time_difference * 1000.0f; + + /* calculate the number of timer intervals that have passed since + the last timer the given timer has been processed -- value is + truncated (rounded down) to an integer */ + int number_of_intervals = (int) (time_difference / Timers[timer].interval); + + /* notify the user in case one or more timer intervals have been + missed */ + if (number_of_intervals > 0) + info("Timer #%d skipped %d interval(s) or %d ms.", timer, number_of_intervals, + number_of_intervals * Timers[timer].interval); + + /* increment the number of passed intervals in order to skip all + missed intervals -- thereby avoiding that unprocessed timers + stack up, continuously update and are notoriously late (certain + railway companies might learn a lesson from us <g>) */ + number_of_intervals++; + + /* calculate time difference between the last time the timer has + been processed and the next time it will be processed */ + int interval = Timers[timer].interval * number_of_intervals; + + /* convert time difference (in milliseconds) to a "timeval" + struct (in seconds and microseconds) */ + struct timeval tv_interval = { + .tv_sec = interval / 1000, + .tv_usec = (interval % 1000) * 1000 + }; + + /* finally, add time difference to the timer's trigger */ + timeradd(&Timers[timer].when, &tv_interval, &Timers[timer].when); +} + + +int timer_remove(void (*callback) (void *data), void *data) +/* Remove a timer with given callback and data. + + callback (void pointer): function of type void func(void *data); + here, it will be used to identify the timer + + data (void pointer): data which will be passed to the callback + function; here, it will be used to identify the timer + + return value (integer): returns a value of 0 on successful timer + removal; otherwise returns a value of -1 +*/ { - tv->tv_sec += msec / 1000; - tv->tv_usec += (msec - 1000 * (msec / 1000)) * 1000; + int timer; /* current timer's ID */ + + /* loop through the timer slots and try to find the specified + timer slot by looking for its settings */ + for (timer = 0; timer < nTimers; timer++) { + /* skip inactive (i.e. deleted) timers */ + if (Timers[timer].active == 0) + continue; - if (tv->tv_usec >= 1000000) { - tv->tv_usec -= 1000000; - tv->tv_sec++; + if (Timers[timer].callback == callback && Timers[timer].data == data) { + /* we have found the timer slot, so mark it as being inactive; + we will not actually delete the slot, so its allocated + memory may be re-used */ + Timers[timer].active = 0; + + /* signal successful timer removal */ + return 0; + } } + + /* we have NOT found the timer slot, so signal failure by + returning a value of -1 */ + return -1; } int timer_add(void (*callback) (void *data), void *data, const int interval, const int one_shot) -{ - int i; - struct timeval now; +/* Create a new timer and add it to the timer queue. + + callback (void pointer): function of type void func(void *data) + which will be called whenever the timer triggers; this pointer + will also be used to identify a specific timer - /* find a free slot */ - for (i = 0; i < nTimers; i++) { - if (Timers[i].active == 0) + data (void pointer): data which will be passed to the callback + function; this pointer will also be used to identify a specific + timer + + interval (integer): specifies the timer's triggering interval in + milliseconds + + one_shot (integer): specifies whether the timer should trigger + indefinitely until it is deleted (value of 0) or only once (all + other values) + + return value (integer): returns a value of 0 on successful timer + creation; otherwise returns a value of -1 +*/ +{ + int timer; /* current timer's ID */ + struct timeval now; /* struct to hold current time */ + + /* try to minimize memory usage by looping through the timer slots + and looking for an inactive timer */ + for (timer = 0; timer < nTimers; timer++) { + if (Timers[timer].active == 0) { + /* we've just found one, so let's reuse it ("timer" holds its + ID) by breaking the loop */ break; + } } - /* none found, allocate a new slot */ - if (i >= nTimers) { + /* no inactive timers (or none at all) found, so we have to add a + new timer slot */ + if (timer >= nTimers) { + /* increment number of timers and (re-)allocate memory used for + storing the timer slots */ nTimers++; Timers = realloc(Timers, nTimers * sizeof(*Timers)); + + /* make sure "timer" points to valid memory */ + timer = nTimers - 1; + + /* realloc() has failed */ + if (Timers == NULL) { + /* restore old number of timers */ + nTimers--; + + /* signal unsuccessful timer creation */ + return -1; + } } + /* get current time so the timer triggers immediately */ gettimeofday(&now, NULL); - /* fill slot */ - Timers[i].callback = callback; - Timers[i].data = data; - Timers[i].when = now; - Timers[i].interval = interval; - Timers[i].one_shot = one_shot; - Timers[i].active = 1; + /* initialize timer data */ + Timers[timer].callback = callback; + Timers[timer].data = data; + Timers[timer].when = now; + Timers[timer].interval = interval; + Timers[timer].one_shot = one_shot; + + /* set timer to active so that it is processed and not overwritten + by the memory optimization routine above */ + Timers[timer].active = 1; - /* if one-shot timer, don't fire now */ + /* one-shot timers should NOT fire immediately, so delay them by a + single timer interval */ if (one_shot) { - timer_inc(&Timers[i].when, interval); + timer_inc(timer, &now); } + /* signal successful timer creation */ return 0; } +int timer_add_late(void (*callback) (void *data), void *data, const int interval, const int one_shot) +/* This function creates a new timer and adds it to the timer queue + just as timer_add() does, but the timer will NOT be triggered + immediately (useful for scheduling things). + + callback (void pointer): function of type void func(void *data) + which will be called whenever the timer triggers; this pointer + will also be used to identify a specific timer + + data (void pointer): data which will be passed to the callback + function; this pointer will also be used to identify a specific + timer + + interval (integer): specifies the timer's triggering interval in + milliseconds + + one_shot (integer): specifies whether the timer should trigger + indefinitely until it is deleted (value of 0) or only once (all + other values) + + return value (integer): returns a value of 0 on successful timer + creation; otherwise returns a value of -1 +*/ +{ + /* create new timer slot and add it to the timer queue; mask it as + one-shot timer for now, so the timer will be delayed by a + single timer interval */ + if (!timer_add(callback, data, interval, 1)) { + /* signal unsuccessful timer creation */ + return -1; + } + + int timer; /* current timer's ID */ + + /* loop through the timer slots and try to find the new timer slot + by looking for its settings */ + for (timer = 0; timer < nTimers; timer++) { + /* skip inactive (i.e. deleted) timers */ + if (Timers[timer].active == 0) + continue; + + if (Timers[timer].callback == callback && Timers[timer].data == data && Timers[timer].interval == interval) { + /* we have found the new timer slot, so unmask it by setting + its "one_shot" variable to the REAL value; then signal + successful timer creation */ + Timers[timer].one_shot = one_shot; + + /* signal successful timer creation */ + return 0; + } + } + + /* we have NOT found the new timer slot for some reason, so signal + failure by returning a value of -1 */ + return -1; +} + + int timer_process(struct timespec *delay) +/* Process timer queue. + + delay (timespec pointer): struct holding delay till the next + upcoming timer event + + return value (integer): returns a value of 0 when timers have been + processed successfully; otherwise returns a value of -1 +*/ { - int i, flag, min; - struct timeval now; + struct timeval now; /* struct to hold current time */ - /* the current moment */ + /* get current time to check which timers need processing */ gettimeofday(&now, NULL); - /* sanity check */ - if (nTimers == 0) { - error("huh? not one single timer to process? dazed and confused..."); + /* sanity check; by now, at least one timer should be + instantiated */ + if (nTimers <= 0) { + /* otherwise, print an error and return a value of -1 to + signal an error */ + error("Huh? Not even a single timer to process? Dazed and confused..."); return -1; } - /* process expired timers */ - flag = 0; - for (i = 0; i < nTimers; i++) { - if (Timers[i].active == 0) + int timer; /* current timer's ID */ + + /* process all expired timers */ + for (timer = 0; timer < nTimers; timer++) { + /* skip inactive (i.e. deleted) timers */ + if (Timers[timer].active == 0) continue; - if (timercmp(&Timers[i].when, &now, <=)) { - flag = 1; - /* callback */ - if (Timers[i].callback != NULL) { - Timers[i].callback(Timers[i].data); + + /* check whether current timer needs to be processed, i.e. the + timer's triggering time is less than or equal to the current + time; according to the man page of timercmp(), this avoids + using the operators ">=", "<=" and "==" which might be broken + on some systems */ + if (!timercmp(&Timers[timer].when, &now, >)) { + /* if the timer's callback function has been set, call it and + pass the corresponding data */ + if (Timers[timer].callback != NULL) { + Timers[timer].callback(Timers[timer].data); } - /* respawn or delete timer */ - if (Timers[i].one_shot) { - Timers[i].active = 0; + + /* check for one-shot timers */ + if (Timers[timer].one_shot) { + /* mark one-shot timer as inactive (which means the timer has + been deleted and its allocated memory may be re-used) */ + Timers[timer].active = 0; } else { - Timers[i].when = now; - timer_inc(&Timers[i].when, Timers[i].interval); + /* otherwise, re-spawn timer by adding one triggering interval + to its triggering time */ + timer_inc(timer, &now); } } } - /* find next timer */ - flag = 1; - min = -1; - for (i = 0; i < nTimers; i++) { - if (Timers[i].active == 0) + int next_timer = -1; /* ID of the next upcoming timer */ + + /* loop through the timer slots and try to find the next upcoming + timer */ + for (timer = 0; timer < nTimers; timer++) { + /* skip inactive (i.e. deleted) timers */ + if (Timers[timer].active == 0) continue; - if (flag || timercmp(&Timers[i].when, &Timers[min].when, <)) { - flag = 0; - min = i; + + /* if this is the first timer that we check, mark it as the next + upcoming timer; otherwise, we'll have nothing to compare + against in this loop */ + if (next_timer < 0) + next_timer = timer; + /* check whether current timer needs processing prior to the one + selected */ + else if (timercmp(&Timers[timer].when, &Timers[next_timer].when, <)) { + /* if so, mark it as the next upcoming timer */ + next_timer = timer; } } - if (min < 0) { - error("huh? not one single timer left? dazed and confused..."); + /* sanity check; we should by now have found the next upcoming + timer */ + if (next_timer < 0) { + /* otherwise, print an error and return a value of -1 to signal an + error */ + error("Huh? Not even a single timer left? Dazed and confused..."); return -1; } - /* delay until next timer event */ - delay->tv_sec = Timers[min].when.tv_sec - now.tv_sec; - delay->tv_nsec = Timers[min].when.tv_usec - now.tv_usec; - if (delay->tv_nsec < 0) { - delay->tv_sec--; - delay->tv_nsec += 1000000; + /* processing all the timers might have taken a while, so update + the current time to compensate for processing delay */ + gettimeofday(&now, NULL); + + struct timeval diff; /* struct holding the time difference + between current time and the triggering time of the + next upcoming timer event */ + + /* calculate delay to the next upcoming timer event and store it + in "diff" */ + timersub(&Timers[next_timer].when, &now, &diff); + + /* a negative delay has occurred (positive clock skew or some + timers are faster than the time needed for processing their + callbacks) */ + if (diff.tv_sec < 0) { + /* zero "diff" so the next update is triggered immediately */ + timerclear(&diff); + } else { + /* convert "diff" to milliseconds */ + int time_difference = diff.tv_sec * 1000 + diff.tv_usec / 1000; + + /* if there is a notable difference between "time_difference" and + the next upcoming timer's interval, assume clock skew */ + if (time_difference > (Timers[next_timer].interval + CLOCK_SKEW_DETECT_TIME_IN_MS)) { + /* extract clock skew from "time_difference" by eliminating + the timer's triggering interval */ + int skew = time_difference - Timers[next_timer].interval; + + /* display an info message to inform the user */ + info("Oops, clock skewed by %d ms, updating timestamps...", skew); + + /* convert clock skew from milliseconds to "timeval" + structure */ + struct timeval clock_skew = { + .tv_sec = skew / 1000, + .tv_usec = (skew % 1000) * 1000 + }; + + /* process all timers */ + for (timer = 0; timer < nTimers; timer++) { + /* skip inactive (i.e. deleted) timers */ + if (Timers[timer].active == 0) + continue; + + /* correct timer's time stamp by clock skew */ + timersub(&Timers[timer].when, &clock_skew, &Timers[timer].when); + } + + /* finally, zero "diff" so the next update is triggered + immediately */ + timerclear(&diff); + } } - /* nanoseconds!! */ - delay->tv_nsec *= 1000; - return 0; + /* set timespec "delay" passed by calling function to "diff" */ + delay->tv_sec = diff.tv_sec; + /* timespec uses nanoseconds instead of microseconds!!! */ + delay->tv_nsec = diff.tv_usec * 1000; + /* signal successful timer processing */ + return 0; } void timer_exit(void) -{ +/* Release all timers and free the associated memory block. + return value: void +*/ +{ + /* reset number of allocated timer slots */ nTimers = 0; + /* free memory used for storing the timer slots */ if (Timers != NULL) { - free(Timers);; + free(Timers); Timers = NULL; } } |