~siretart/lcd4linux/debian

« back to all changes in this revision

Viewing changes to timer.c

  • Committer: Reinhard Tartler
  • Date: 2011-04-27 17:24:15 UTC
  • mto: This revision was merged to the branch mainline in revision 750.
  • Revision ID: siretart@tauware.de-20110427172415-6n4aptmvmz0eztvm
Tags: upstream-0.11.0~svn1143
ImportĀ upstreamĀ versionĀ 0.11.0~svn1143

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* $Id: timer.c 1143 2011-02-12 22:46:19Z mzuther $
 
2
 * $URL: https://ssl.bulix.org/svn/lcd4linux/trunk/timer.c $
 
3
 *
 
4
 * Generic timer handling.
 
5
 *
 
6
 * Copyright (C) 2003, 2004 Michael Reinelt <michael@reinelt.co.at>
 
7
 * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net>
 
8
 *
 
9
 * This program is free software; you can redistribute it and/or modify
 
10
 * it under the terms of the GNU General Public License as published by
 
11
 * the Free Software Foundation; either version 2, or (at your option)
 
12
 * any later version.
 
13
 *
 
14
 * This program is distributed in the hope that it will be useful,
 
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
17
 * GNU General Public License for more details.
 
18
 *
 
19
 * You should have received a copy of the GNU General Public License
 
20
 * along with this program; if not, write to the Free Software
 
21
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
22
 *
 
23
 */
 
24
 
 
25
/*
 
26
 * Exported functions:
 
27
 *
 
28
 * int timer_add(void (*callback) (void *data), void *data, const int
 
29
 *     interval, const int one_shot)
 
30
 *
 
31
 *   Create a new timer and add it to the timer queue.
 
32
 *
 
33
 *
 
34
 * int timer_add_late(void (*callback) (void *data), void *data, const
 
35
 *     int interval, const int one_shot)
 
36
 *
 
37
 *   This function creates a new timer and adds it to the timer queue
 
38
 *   just as timer_add() does, but the timer will NOT be triggered
 
39
 *   immediately (useful for scheduling things).
 
40
 *
 
41
 *
 
42
 * int timer_process(struct timespec *delay)
 
43
 *
 
44
 *    Process timer queue.
 
45
 *
 
46
 *
 
47
 * int timer_remove(void (*callback) (void *data), void *data)
 
48
 *
 
49
 *   Remove a new timer with given callback and data.
 
50
 *
 
51
 *
 
52
 * void timer_exit(void)
 
53
 *
 
54
 *   Release all timers and free the associated memory block.
 
55
 *
 
56
 */
 
57
 
 
58
 
 
59
#include "config.h"
 
60
 
 
61
#include <stdlib.h>
 
62
#include <stdio.h>
 
63
#include <string.h>
 
64
#include <sys/time.h>
 
65
#include <time.h>
 
66
 
 
67
#include "debug.h"
 
68
#include "cfg.h"
 
69
#include "timer.h"
 
70
 
 
71
#ifdef WITH_DMALLOC
 
72
#include <dmalloc.h>
 
73
#endif
 
74
 
 
75
/* threshold in milliseconds that differentiates between clock skew
 
76
   and clock jitter */
 
77
#define CLOCK_SKEW_DETECT_TIME_IN_MS 1000
 
78
 
 
79
/* structure for storing all relevant data of a single timer */
 
80
typedef struct TIMER {
 
81
    /* pointer to function of type void func(void *data) that will be
 
82
       called when the timer is processed; it will also be used to
 
83
       identify a specific timer */
 
84
    void (*callback) (void *data);
 
85
 
 
86
    /* pointer to data which will be passed to the callback function;
 
87
       it will also be used to identify a specific timer */
 
88
    void *data;
 
89
 
 
90
    /* struct to hold the time (in seconds and milliseconds since the
 
91
       Epoch) when the timer will be processed for the next time */
 
92
    struct timeval when;
 
93
 
 
94
    /* specifies the timer's triggering interval in milliseconds */
 
95
    int interval;
 
96
 
 
97
    /* specifies whether the timer should trigger indefinitely until
 
98
       it is deleted (value of 0) or only once (all other values) */
 
99
    int one_shot;
 
100
 
 
101
    /* marks timer as being active (so it will get processed) or
 
102
       inactive (which means the timer has been deleted and its
 
103
       allocated memory may be re-used) */
 
104
    int active;
 
105
} TIMER;
 
106
 
 
107
/* number of allocated timer slots */
 
108
int nTimers = 0;
 
109
 
 
110
/* pointer to memory allocated for storing the timer slots */
 
111
TIMER *Timers = NULL;
 
112
 
 
113
 
 
114
static void timer_inc(const int timer, struct timeval *now)
 
115
/*  Update the time a given timer updates next.
 
116
 
 
117
    timer (integer): internal ID of timer that is to be updated
 
118
 
 
119
        now (timeval pointer): struct holding the "current" time
 
120
 
 
121
        return value: void
 
122
 */
 
123
{
 
124
    /* calculate the time difference between the last time the given
 
125
       timer has been processed and the current time */
 
126
    struct timeval diff;
 
127
    timersub(now, &Timers[timer].when, &diff);
 
128
 
 
129
    /* convert this time difference to fractional seconds */
 
130
    float time_difference = diff.tv_sec + diff.tv_usec / 1000000.0f;
 
131
 
 
132
    /* convert time difference to fractional milliseconds */
 
133
    time_difference = time_difference * 1000.0f;
 
134
 
 
135
    /* calculate the number of timer intervals that have passed since
 
136
       the last timer the given timer has been processed -- value is
 
137
       truncated (rounded down) to an integer */
 
138
    int number_of_intervals = (int) (time_difference / Timers[timer].interval);
 
139
 
 
140
    /* notify the user in case one or more timer intervals have been
 
141
       missed */
 
142
    if (number_of_intervals > 0)
 
143
        info("Timer #%d skipped %d interval(s) or %d ms.", timer, number_of_intervals,
 
144
             number_of_intervals * Timers[timer].interval);
 
145
 
 
146
    /* increment the number of passed intervals in order to skip all
 
147
       missed intervals -- thereby avoiding that unprocessed timers
 
148
       stack up, continuously update and are notoriously late (certain
 
149
       railway companies might learn a lesson from us <g>) */
 
150
    number_of_intervals++;
 
151
 
 
152
    /* calculate time difference between the last time the timer has
 
153
       been processed and the next time it will be processed */
 
154
    int interval = Timers[timer].interval * number_of_intervals;
 
155
 
 
156
    /* convert time difference (in milliseconds) to a "timeval"
 
157
       struct (in seconds and microseconds) */
 
158
    struct timeval tv_interval = {
 
159
        .tv_sec = interval / 1000,
 
160
        .tv_usec = (interval % 1000) * 1000
 
161
    };
 
162
 
 
163
    /* finally, add time difference to the timer's trigger */
 
164
    timeradd(&Timers[timer].when, &tv_interval, &Timers[timer].when);
 
165
}
 
166
 
 
167
 
 
168
int timer_remove(void (*callback) (void *data), void *data)
 
169
/*  Remove a timer with given callback and data.
 
170
 
 
171
    callback (void pointer): function of type void func(void *data);
 
172
    here, it will be used to identify the timer
 
173
 
 
174
        data (void pointer): data which will be passed to the callback
 
175
        function; here, it will be used to identify the timer
 
176
 
 
177
        return value (integer): returns a value of 0 on successful timer
 
178
        removal; otherwise returns a value of -1
 
179
*/
 
180
{
 
181
    int timer;                  /* current timer's ID */
 
182
 
 
183
    /* loop through the timer slots and try to find the specified
 
184
       timer slot by looking for its settings */
 
185
    for (timer = 0; timer < nTimers; timer++) {
 
186
        /* skip inactive (i.e. deleted) timers */
 
187
        if (Timers[timer].active == 0)
 
188
            continue;
 
189
 
 
190
        if (Timers[timer].callback == callback && Timers[timer].data == data) {
 
191
            /* we have found the timer slot, so mark it as being inactive;
 
192
               we will not actually delete the slot, so its allocated
 
193
               memory may be re-used */
 
194
            Timers[timer].active = 0;
 
195
 
 
196
            /* signal successful timer removal */
 
197
            return 0;
 
198
        }
 
199
    }
 
200
 
 
201
    /* we have NOT found the timer slot, so signal failure by
 
202
       returning a value of -1 */
 
203
    return -1;
 
204
}
 
205
 
 
206
 
 
207
int timer_add(void (*callback) (void *data), void *data, const int interval, const int one_shot)
 
208
/*  Create a new timer and add it to the timer queue.
 
209
 
 
210
    callback (void pointer): function of type void func(void *data)
 
211
        which will be called whenever the timer triggers; this pointer
 
212
        will also be used to identify a specific timer
 
213
 
 
214
        data (void pointer): data which will be passed to the callback
 
215
        function; this pointer will also be used to identify a specific
 
216
        timer
 
217
 
 
218
        interval (integer): specifies the timer's triggering interval in
 
219
        milliseconds
 
220
 
 
221
        one_shot (integer): specifies whether the timer should trigger
 
222
        indefinitely until it is deleted (value of 0) or only once (all
 
223
        other values)
 
224
 
 
225
        return value (integer): returns a value of 0 on successful timer
 
226
        creation; otherwise returns a value of -1
 
227
*/
 
228
{
 
229
    int timer;                  /* current timer's ID */
 
230
    struct timeval now;         /* struct to hold current time */
 
231
 
 
232
    /* try to minimize memory usage by looping through the timer slots
 
233
       and looking for an inactive timer */
 
234
    for (timer = 0; timer < nTimers; timer++) {
 
235
        if (Timers[timer].active == 0) {
 
236
            /* we've just found one, so let's reuse it ("timer" holds its
 
237
               ID) by breaking the loop */
 
238
            break;
 
239
        }
 
240
    }
 
241
 
 
242
    /* no inactive timers (or none at all) found, so we have to add a
 
243
       new timer slot */
 
244
    if (timer >= nTimers) {
 
245
        /* increment number of timers and (re-)allocate memory used for
 
246
           storing the timer slots */
 
247
        nTimers++;
 
248
        Timers = realloc(Timers, nTimers * sizeof(*Timers));
 
249
 
 
250
        /* make sure "timer" points to valid memory */
 
251
        timer = nTimers - 1;
 
252
 
 
253
        /* realloc() has failed */
 
254
        if (Timers == NULL) {
 
255
            /* restore old number of timers */
 
256
            nTimers--;
 
257
 
 
258
            /* signal unsuccessful timer creation */
 
259
            return -1;
 
260
        }
 
261
    }
 
262
 
 
263
    /* get current time so the timer triggers immediately */
 
264
    gettimeofday(&now, NULL);
 
265
 
 
266
    /* initialize timer data */
 
267
    Timers[timer].callback = callback;
 
268
    Timers[timer].data = data;
 
269
    Timers[timer].when = now;
 
270
    Timers[timer].interval = interval;
 
271
    Timers[timer].one_shot = one_shot;
 
272
 
 
273
    /* set timer to active so that it is processed and not overwritten
 
274
       by the memory optimization routine above */
 
275
    Timers[timer].active = 1;
 
276
 
 
277
    /* one-shot timers should NOT fire immediately, so delay them by a
 
278
       single timer interval */
 
279
    if (one_shot) {
 
280
        timer_inc(timer, &now);
 
281
    }
 
282
 
 
283
    /* signal successful timer creation */
 
284
    return 0;
 
285
}
 
286
 
 
287
 
 
288
int timer_add_late(void (*callback) (void *data), void *data, const int interval, const int one_shot)
 
289
/*  This function creates a new timer and adds it to the timer queue
 
290
        just as timer_add() does, but the timer will NOT be triggered
 
291
        immediately (useful for scheduling things).
 
292
 
 
293
        callback (void pointer): function of type void func(void *data)
 
294
        which will be called whenever the timer triggers; this pointer
 
295
        will also be used to identify a specific timer
 
296
 
 
297
        data (void pointer): data which will be passed to the callback
 
298
        function; this pointer will also be used to identify a specific
 
299
        timer
 
300
 
 
301
        interval (integer): specifies the timer's triggering interval in
 
302
        milliseconds
 
303
 
 
304
        one_shot (integer): specifies whether the timer should trigger
 
305
        indefinitely until it is deleted (value of 0) or only once (all
 
306
        other values)
 
307
 
 
308
        return value (integer): returns a value of 0 on successful timer
 
309
        creation; otherwise returns a value of -1
 
310
*/
 
311
{
 
312
    /* create new timer slot and add it to the timer queue; mask it as
 
313
       one-shot timer for now, so the timer will be delayed by a
 
314
       single timer interval */
 
315
    if (!timer_add(callback, data, interval, 1)) {
 
316
        /* signal unsuccessful timer creation */
 
317
        return -1;
 
318
    }
 
319
 
 
320
    int timer;                  /* current timer's ID */
 
321
 
 
322
    /* loop through the timer slots and try to find the new timer slot
 
323
       by looking for its settings */
 
324
    for (timer = 0; timer < nTimers; timer++) {
 
325
        /* skip inactive (i.e. deleted) timers */
 
326
        if (Timers[timer].active == 0)
 
327
            continue;
 
328
 
 
329
        if (Timers[timer].callback == callback && Timers[timer].data == data && Timers[timer].interval == interval) {
 
330
            /* we have found the new timer slot, so unmask it by setting
 
331
               its "one_shot" variable to the REAL value; then signal
 
332
               successful timer creation */
 
333
            Timers[timer].one_shot = one_shot;
 
334
 
 
335
            /* signal successful timer creation */
 
336
            return 0;
 
337
        }
 
338
    }
 
339
 
 
340
    /* we have NOT found the new timer slot for some reason, so signal
 
341
       failure by returning a value of -1 */
 
342
    return -1;
 
343
}
 
344
 
 
345
 
 
346
int timer_process(struct timespec *delay)
 
347
/*  Process timer queue.
 
348
 
 
349
        delay (timespec pointer): struct holding delay till the next
 
350
        upcoming timer event
 
351
 
 
352
        return value (integer): returns a value of 0 when timers have been
 
353
        processed successfully; otherwise returns a value of -1
 
354
*/
 
355
{
 
356
    struct timeval now;         /* struct to hold current time */
 
357
 
 
358
    /* get current time to check which timers need processing */
 
359
    gettimeofday(&now, NULL);
 
360
 
 
361
    /* sanity check; by now, at least one timer should be
 
362
       instantiated */
 
363
    if (nTimers <= 0) {
 
364
        /* otherwise, print an error and return a value of -1 to
 
365
           signal an error */
 
366
        error("Huh? Not even a single timer to process? Dazed and confused...");
 
367
        return -1;
 
368
    }
 
369
 
 
370
    int timer;                  /* current timer's ID */
 
371
 
 
372
    /* process all expired timers */
 
373
    for (timer = 0; timer < nTimers; timer++) {
 
374
        /* skip inactive (i.e. deleted) timers */
 
375
        if (Timers[timer].active == 0)
 
376
            continue;
 
377
 
 
378
        /* check whether current timer needs to be processed, i.e. the
 
379
           timer's triggering time is less than or equal to the current
 
380
           time; according to the man page of timercmp(), this avoids
 
381
           using the operators ">=", "<=" and "==" which might be broken
 
382
           on some systems */
 
383
        if (!timercmp(&Timers[timer].when, &now, >)) {
 
384
            /* if the timer's callback function has been set, call it and
 
385
               pass the corresponding data */
 
386
            if (Timers[timer].callback != NULL) {
 
387
                Timers[timer].callback(Timers[timer].data);
 
388
            }
 
389
 
 
390
            /* check for one-shot timers */
 
391
            if (Timers[timer].one_shot) {
 
392
                /* mark one-shot timer as inactive (which means the timer has
 
393
                   been deleted and its allocated memory may be re-used) */
 
394
                Timers[timer].active = 0;
 
395
            } else {
 
396
                /* otherwise, re-spawn timer by adding one triggering interval
 
397
                   to its triggering time */
 
398
                timer_inc(timer, &now);
 
399
            }
 
400
        }
 
401
    }
 
402
 
 
403
    int next_timer = -1;        /* ID of the next upcoming timer */
 
404
 
 
405
    /* loop through the timer slots and try to find the next upcoming
 
406
       timer */
 
407
    for (timer = 0; timer < nTimers; timer++) {
 
408
        /* skip inactive (i.e. deleted) timers */
 
409
        if (Timers[timer].active == 0)
 
410
            continue;
 
411
 
 
412
        /* if this is the first timer that we check, mark it as the next
 
413
           upcoming timer; otherwise, we'll have nothing to compare
 
414
           against in this loop */
 
415
        if (next_timer < 0)
 
416
            next_timer = timer;
 
417
        /* check whether current timer needs processing prior to the one
 
418
           selected */
 
419
        else if (timercmp(&Timers[timer].when, &Timers[next_timer].when, <)) {
 
420
            /* if so, mark it as the next upcoming timer */
 
421
            next_timer = timer;
 
422
        }
 
423
    }
 
424
 
 
425
    /* sanity check; we should by now have found the next upcoming
 
426
       timer */
 
427
    if (next_timer < 0) {
 
428
        /* otherwise, print an error and return a value of -1 to signal an
 
429
           error */
 
430
        error("Huh? Not even a single timer left? Dazed and confused...");
 
431
        return -1;
 
432
    }
 
433
 
 
434
    /* processing all the timers might have taken a while, so update
 
435
       the current time to compensate for processing delay */
 
436
    gettimeofday(&now, NULL);
 
437
 
 
438
    struct timeval diff;        /* struct holding the time difference
 
439
                                   between current time and the triggering time of the
 
440
                                   next upcoming timer event */
 
441
 
 
442
    /* calculate delay to the next upcoming timer event and store it
 
443
       in "diff" */
 
444
    timersub(&Timers[next_timer].when, &now, &diff);
 
445
 
 
446
    /* a negative delay has occurred (positive clock skew or some
 
447
       timers are faster than the time needed for processing their
 
448
       callbacks) */
 
449
    if (diff.tv_sec < 0) {
 
450
        /* zero "diff" so the next update is triggered immediately */
 
451
        timerclear(&diff);
 
452
    } else {
 
453
        /* convert "diff" to milliseconds */
 
454
        int time_difference = diff.tv_sec * 1000 + diff.tv_usec / 1000;
 
455
 
 
456
        /* if there is a notable difference between "time_difference" and
 
457
           the next upcoming timer's interval, assume clock skew */
 
458
        if (time_difference > (Timers[next_timer].interval + CLOCK_SKEW_DETECT_TIME_IN_MS)) {
 
459
            /* extract clock skew from "time_difference" by eliminating
 
460
               the timer's triggering interval */
 
461
            int skew = time_difference - Timers[next_timer].interval;
 
462
 
 
463
            /* display an info message to inform the user */
 
464
            info("Oops, clock skewed by %d ms, updating timestamps...", skew);
 
465
 
 
466
            /* convert clock skew from milliseconds to "timeval"
 
467
               structure */
 
468
            struct timeval clock_skew = {
 
469
                .tv_sec = skew / 1000,
 
470
                .tv_usec = (skew % 1000) * 1000
 
471
            };
 
472
 
 
473
            /* process all timers */
 
474
            for (timer = 0; timer < nTimers; timer++) {
 
475
                /* skip inactive (i.e. deleted) timers */
 
476
                if (Timers[timer].active == 0)
 
477
                    continue;
 
478
 
 
479
                /* correct timer's time stamp by clock skew */
 
480
                timersub(&Timers[timer].when, &clock_skew, &Timers[timer].when);
 
481
            }
 
482
 
 
483
            /* finally, zero "diff" so the next update is triggered
 
484
               immediately */
 
485
            timerclear(&diff);
 
486
        }
 
487
    }
 
488
 
 
489
    /* set timespec "delay" passed by calling function to "diff" */
 
490
    delay->tv_sec = diff.tv_sec;
 
491
    /* timespec uses nanoseconds instead of microseconds!!! */
 
492
    delay->tv_nsec = diff.tv_usec * 1000;
 
493
 
 
494
    /* signal successful timer processing */
 
495
    return 0;
 
496
}
 
497
 
 
498
 
 
499
void timer_exit(void)
 
500
/*  Release all timers and free the associated memory block.
 
501
 
 
502
        return value: void
 
503
*/
 
504
{
 
505
    /* reset number of allocated timer slots */
 
506
    nTimers = 0;
 
507
 
 
508
    /* free memory used for storing the timer slots */
 
509
    if (Timers != NULL) {
 
510
        free(Timers);
 
511
        Timers = NULL;
 
512
    }
 
513
}