source: branches/ithildin-1.1/source/timer.c @ 578

Revision 578, 5.3 KB checked in by wd, 7 years ago (diff)

Expand tabs ('\t') to eight spaces.

  • Property svn:keywords set to Id Rev
Line 
1/*
2 * timer.c: the timer system functions
3 *
4 * Copyright 2002 the Ithildin Project.
5 * See the COPYING file for more information on licensing and use.
6 *
7 * This file contains the functions necessary to create and manage timers.
8 * Currently timers have only second-level resolution (i.e. there are no
9 * sub-second timers).  Timers are referred to using a 64-bit reference id.
10 * This allows for 2^64 timers to be created before there is any danger of
11 * overlap.
12 */
13
14#include <ithildin/stand.h>
15
16IDSTRING(rcsid, "$Id$");
17
18/* this stores the reference count for timers and whether or not the reference
19 * count has rolled over. */
20static timer_ref_t timer_refcnt;
21bool timer_rollover;
22
23#define TIMER_DEAD -1
24#define TIMER_REPEAT -2
25
26static timer_event_t *find_timer(timer_ref_t);
27static inline void insert_timer(timer_event_t *);
28
29/* This creates a new timer which repeats for the given count (0 means no
30 * repetition, negative values mean infinite repetition) at the given interval
31 * calling the given function at each execution of the timer.  It returns the
32 * reference id of the timer so that it can be destroyed later if necessary.
33 * The reference id is given to avoid passing back a timer_event structure that
34 * may disappear and become invalid during execution. */
35timer_ref_t create_timer(int rep, time_t interval, hook_function_t callback,
36        void *udata) {
37    timer_event_t *tep = malloc(sizeof(timer_event_t));
38
39    if (timer_rollover) {
40        /* if the timer ref count has rolled over we need to make sure we
41         * assign a unique reference id. */
42        tep->ref = timer_refcnt++;
43        while (find_timer(tep->ref) != NULL)
44            tep->ref = timer_refcnt++;
45    } else {
46        tep->ref = timer_refcnt++;
47        if (timer_refcnt == 0)
48            timer_rollover = true;
49        if (tep->ref == TIMER_INVALID) {
50            free(tep);
51            return create_timer(rep, interval, callback, udata);
52        }
53    }
54
55    if (rep < 0)
56        tep->reps = TIMER_REPEAT;
57    else
58        tep->reps = rep;
59    tep->interval = interval;
60    tep->next = me.now + tep->interval;
61    tep->callback = callback;
62    tep->udata = udata;
63
64    insert_timer(tep);
65
66    return tep->ref;
67}
68
69/* This function looks for a timer with the given reference id and returns it
70 * if it is found. */
71static timer_event_t *find_timer(timer_ref_t ref) {
72    timer_event_t *tep;
73
74    LIST_FOREACH(tep, &me.timers, lp) {
75        if (tep->ref == ref)
76            return tep;
77    }
78
79    return NULL;
80}
81
82/* This destroys the timer with the given reference id.  It is possible (but
83 * rather unlikely) that the wrong timer can be destroyed here because of a
84 * previously assigned reference id. */
85void destroy_timer(timer_ref_t ref) {
86    timer_event_t *tep = find_timer(ref);
87
88    if (tep == NULL)
89        return;
90
91    LIST_REMOVE(tep, lp);
92    free(tep);
93}
94
95/* this allows the caller to adjust the settings of a timer (specifically the
96 * repeat count and the time it goes off).  the third argument is the execution
97 * time from 'now' (the current time). */
98void adjust_timer(timer_ref_t tref, int reps, time_t interval) {
99    timer_event_t *tep = find_timer(tref);
100
101    if (tep == NULL)
102        return;
103
104    LIST_REMOVE(tep, lp);
105    tep->reps = (reps < 0 ? TIMER_REPEAT : reps);
106    tep->interval = interval;
107    tep->next = me.now + interval;
108    insert_timer(tep);
109}
110
111/* This function executes each timer that needs to be called and re-orders the
112 * list as necessary. */
113time_t exec_timers(void) {
114    timer_event_t *tep, *tep2;
115
116    tep = LIST_FIRST(&me.timers);
117    me.now = time(NULL);
118    while (tep != NULL) {
119        tep2 = LIST_NEXT(tep, lp);
120        if (tep->next <= me.now) {
121            tep->callback(NULL, tep->udata);
122            /* now see if the timer needs to be deleted.  if not, then we
123             * re-calculate next and re-position the timer in the list. */
124            if (tep->reps != TIMER_REPEAT && --tep->reps == TIMER_DEAD)
125                destroy_timer(tep->ref);
126            else {
127                if ((tep->next += tep->interval) < me.now)
128                    tep->next = me.now + tep->interval;
129                LIST_REMOVE(tep, lp);
130                insert_timer(tep);
131            }
132        } else
133            /* the list is sorted, so if the condition above is false all the
134             * timers at and beyond this point will not need to go off at this
135             * go-around */
136            break;
137        tep = tep2;
138        me.now = time(NULL);
139    }
140
141    /* We've executed all the timers we needed to... see when the next one (if
142     * any) will need to go off. */
143    if (LIST_EMPTY(&me.timers))
144        return 0;
145    else
146        return ((timer_event_t *)LIST_FIRST(&me.timers))->next - me.now;
147}
148
149/* This inserts a timer into the correct position in the timer list.  That is,
150 * it sorts the timer list by order of execution. */
151static inline void insert_timer(timer_event_t *timer) {
152    timer_event_t *tep;
153
154    tep = LIST_FIRST(&me.timers);
155    if (tep == NULL) {
156        LIST_INSERT_HEAD(&me.timers, timer, lp);
157        return;
158    }
159
160    while (tep != NULL) {
161        if (tep->next > timer->next) {
162            LIST_INSERT_BEFORE(tep, timer, lp);
163            return;
164        }
165        if (LIST_NEXT(tep, lp) == NULL)
166            break;
167        tep = LIST_NEXT(tep, lp);
168    }
169    LIST_INSERT_AFTER(tep, timer, lp);
170}
171
172/* vi:set ts=8 sts=4 sw=4 tw=76 et: */
Note: See TracBrowser for help on using the repository browser.