aboutsummaryrefslogtreecommitdiffstats
path: root/timer.c
diff options
context:
space:
mode:
Diffstat (limited to 'timer.c')
-rw-r--r--timer.c472
1 files changed, 395 insertions, 77 deletions
diff --git a/timer.c b/timer.c
index 604b0f4..b5446c2 100644
--- a/timer.c
+++ b/timer.c
@@ -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;
}
}