From 181cec4348da40331b3e8ab365732c025ec149b2 Mon Sep 17 00:00:00 2001 From: Reinhard Tartler Date: Wed, 27 Apr 2011 19:24:15 +0200 Subject: Import upstream version 0.11.0~svn1143 --- timer.c | 513 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 513 insertions(+) create mode 100644 timer.c (limited to 'timer.c') diff --git a/timer.c b/timer.c new file mode 100644 index 0000000..b5446c2 --- /dev/null +++ b/timer.c @@ -0,0 +1,513 @@ +/* $Id: timer.c 1143 2011-02-12 22:46:19Z mzuther $ + * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/timer.c $ + * + * Generic timer handling. + * + * Copyright (C) 2003, 2004 Michael Reinelt + * Copyright (C) 2004 The LCD4Linux Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * Exported functions: + * + * int timer_add(void (*callback) (void *data), void *data, const int + * interval, const int one_shot) + * + * Create a new timer and add it to the timer queue. + * + * + * 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. + * + */ + + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "debug.h" +#include "cfg.h" +#include "timer.h" + +#ifdef WITH_DMALLOC +#include +#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; + + +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 ) */ + 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 +*/ +{ + 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 (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) +/* 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 + + 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; + } + } + + /* 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); + + /* 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; + + /* one-shot timers should NOT fire immediately, so delay them by a + single timer interval */ + if (one_shot) { + 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 +*/ +{ + struct timeval now; /* struct to hold current time */ + + /* get current time to check which timers need processing */ + gettimeofday(&now, NULL); + + /* 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; + } + + 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; + + /* 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); + } + + /* 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 { + /* otherwise, re-spawn timer by adding one triggering interval + to its triggering time */ + timer_inc(timer, &now); + } + } + } + + 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 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; + } + } + + /* 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; + } + + /* 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); + } + } + + /* 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); + Timers = NULL; + } +} -- cgit v1.2.3