1 |
673 |
markom |
/*
|
2 |
|
|
* Copyright (c) 2000 Alex Holden <alex@linuxhacker.org>
|
3 |
|
|
*
|
4 |
|
|
* This file implements the device independant timer functions.
|
5 |
|
|
*
|
6 |
|
|
* When a part of the server wishes to set a timer, it should call the
|
7 |
|
|
* GdAddTimer() function with the timeout parameter set to the number of
|
8 |
|
|
* milliseconds before the timer should activate, the callback argument
|
9 |
|
|
* set to the function which should be called when the timer expires, and
|
10 |
|
|
* the arg argument set to the (void * type) argument which should be supplied
|
11 |
|
|
* to the timer handler function. The GdAddTimer() returns a pointer to the
|
12 |
|
|
* timer structure * which was created (or NULL if the creation failed for
|
13 |
|
|
* some reason). The prototype for the callback function should look like:
|
14 |
|
|
* void callbackfn(void *arg);
|
15 |
|
|
*
|
16 |
|
|
* If a part of the server wishes to destroy a timer before it has expired
|
17 |
|
|
* (it is not necessary to do so after the timer has expired, as the timer
|
18 |
|
|
* structure is automatically destroyed after the callback function is called),
|
19 |
|
|
* it should call the GdDestroyTimer() function with the address of the timer
|
20 |
|
|
* structure (which was returned by GdAddTimer()).
|
21 |
|
|
*
|
22 |
|
|
* If a part of the server wishes to destroy a timer but does not know the
|
23 |
|
|
* address of it's timer structure, it can call GdFindTimer() with the
|
24 |
|
|
* callback argument as a parameter. The argument must be unique to that
|
25 |
|
|
* timer (the address of a structure or function is probably a good choice).
|
26 |
|
|
* This function returns the address of the first timer found with that
|
27 |
|
|
* argument, or NULL if no matching timer was found.
|
28 |
|
|
*
|
29 |
|
|
* The main select() loop needs to be called with a timeout obtained using the
|
30 |
|
|
* GdGetNextTimeout(). GdGetNextTimeout() is called with the event loop
|
31 |
|
|
* timeout in ms, and fills in the specified timeout structure, which should
|
32 |
|
|
* be used as the argument to the select() call. The timeout returned by the
|
33 |
|
|
* GdGetNextTimeout() call is decided by looking through the timer list for
|
34 |
|
|
* the timer with the shortest amount of time remaining, and also at the
|
35 |
|
|
* maximum delay parameter. If there are no timers on the timer list and the
|
36 |
|
|
* timeout argument is 0, it will return FALSE, otherwise it will return TRUE.
|
37 |
|
|
*
|
38 |
|
|
* When the main select() loop times out, the GdTimeout() function should be
|
39 |
|
|
* called. This will go through the timer list and call the callback functions
|
40 |
|
|
* of all timers which have expired, then remove them from the timer list. At
|
41 |
|
|
* the same time, you should check the value of the maximum timeout parameter
|
42 |
|
|
* to see if it has expired (in which case you can then return to the client
|
43 |
|
|
* with a timeout event). This function returns TRUE if the timeout specified in
|
44 |
|
|
* the last GdGetNextTimeout() call has expired, or FALSE otherwise.
|
45 |
|
|
*
|
46 |
|
|
* Note that no guarantees can be made as to when exactly the timer callback
|
47 |
|
|
* will be called as it depends on how often the GdTimeout() function is
|
48 |
|
|
* called and how long any other timeouts in the queue before you take to
|
49 |
|
|
* complete. Especially in the case where the client is linked into the server,
|
50 |
|
|
* the client must call into the server on a regular basis, otherwise the
|
51 |
|
|
* timers may run late.
|
52 |
|
|
*/
|
53 |
|
|
|
54 |
|
|
#include <stdio.h>
|
55 |
|
|
#include <unistd.h>
|
56 |
|
|
#include <stdlib.h>
|
57 |
|
|
#include "device.h"
|
58 |
|
|
|
59 |
|
|
static MWTIMER *timerlist = NULL;
|
60 |
|
|
static struct timeval mainloop_timeout;
|
61 |
|
|
static struct timeval current_time;
|
62 |
|
|
|
63 |
|
|
static void calculate_timeval(struct timeval *tv, MWTIMEOUT to);
|
64 |
|
|
static signed long time_to_expiry(struct timeval *t);
|
65 |
|
|
|
66 |
|
|
MWTIMER *GdAddTimer(MWTIMEOUT timeout, MWTIMERCB callback, void *arg)
|
67 |
|
|
{
|
68 |
|
|
MWTIMER *newtimer;
|
69 |
|
|
|
70 |
|
|
if(!(newtimer = malloc(sizeof(MWTIMER)))) return NULL;
|
71 |
|
|
|
72 |
|
|
gettimeofday(¤t_time, NULL);
|
73 |
|
|
|
74 |
|
|
if(timerlist) timerlist->prev = newtimer;
|
75 |
|
|
|
76 |
|
|
calculate_timeval(&newtimer->timeout, timeout);
|
77 |
|
|
newtimer->callback = callback;
|
78 |
|
|
newtimer->arg = arg;
|
79 |
|
|
newtimer->next = timerlist;
|
80 |
|
|
newtimer->prev = NULL;
|
81 |
|
|
newtimer->type = MWTIMER_ONESHOT;
|
82 |
|
|
newtimer->period = timeout;
|
83 |
|
|
timerlist = newtimer;
|
84 |
|
|
|
85 |
|
|
return newtimer;
|
86 |
|
|
}
|
87 |
|
|
|
88 |
|
|
MWTIMER *GdAddPeriodicTimer(MWTIMEOUT timeout, MWTIMERCB callback, void *arg)
|
89 |
|
|
{
|
90 |
|
|
MWTIMER *newtimer;
|
91 |
|
|
|
92 |
|
|
if(!(newtimer = malloc(sizeof(MWTIMER)))) return NULL;
|
93 |
|
|
|
94 |
|
|
gettimeofday (¤t_time, NULL);
|
95 |
|
|
|
96 |
|
|
if (timerlist) timerlist->prev = newtimer;
|
97 |
|
|
|
98 |
|
|
calculate_timeval (&newtimer->timeout, timeout);
|
99 |
|
|
newtimer->callback = callback;
|
100 |
|
|
newtimer->arg = arg;
|
101 |
|
|
newtimer->next = timerlist;
|
102 |
|
|
newtimer->prev = NULL;
|
103 |
|
|
newtimer->type = MWTIMER_PERIODIC;
|
104 |
|
|
newtimer->period = timeout;
|
105 |
|
|
timerlist = newtimer;
|
106 |
|
|
|
107 |
|
|
return newtimer;
|
108 |
|
|
}
|
109 |
|
|
|
110 |
|
|
void GdDestroyTimer(MWTIMER *timer)
|
111 |
|
|
{
|
112 |
|
|
if(timer->next) timer->next->prev = timer->prev;
|
113 |
|
|
if(timer->prev) timer->prev->next = timer->next;
|
114 |
|
|
if(timer == timerlist) {
|
115 |
|
|
if(timer->next) timerlist = timer->next;
|
116 |
|
|
else timerlist = timer->prev;
|
117 |
|
|
}
|
118 |
|
|
free(timer);
|
119 |
|
|
}
|
120 |
|
|
|
121 |
|
|
MWTIMER *GdFindTimer(void *arg)
|
122 |
|
|
{
|
123 |
|
|
MWTIMER *t = timerlist;
|
124 |
|
|
|
125 |
|
|
while(t) {
|
126 |
|
|
if(t->arg == arg) break;
|
127 |
|
|
t = t->next;
|
128 |
|
|
}
|
129 |
|
|
|
130 |
|
|
return t;
|
131 |
|
|
}
|
132 |
|
|
|
133 |
|
|
MWBOOL GdGetNextTimeout(struct timeval *tv, MWTIMEOUT timeout)
|
134 |
|
|
{
|
135 |
|
|
signed long i, lowest_timeout;
|
136 |
|
|
MWTIMER *t = timerlist;
|
137 |
|
|
|
138 |
|
|
if(!timeout && !timerlist) return FALSE;
|
139 |
|
|
|
140 |
|
|
gettimeofday(¤t_time, NULL);
|
141 |
|
|
|
142 |
|
|
if(timeout) {
|
143 |
|
|
calculate_timeval(&mainloop_timeout, timeout);
|
144 |
|
|
lowest_timeout = time_to_expiry(&mainloop_timeout);
|
145 |
|
|
} else {
|
146 |
|
|
lowest_timeout = time_to_expiry(&t->timeout);
|
147 |
|
|
mainloop_timeout.tv_sec = -1;
|
148 |
|
|
t = t->next;
|
149 |
|
|
}
|
150 |
|
|
|
151 |
|
|
while(t) {
|
152 |
|
|
i = time_to_expiry(&t->timeout);
|
153 |
|
|
if(i < lowest_timeout) lowest_timeout = i;
|
154 |
|
|
t = t->next;
|
155 |
|
|
}
|
156 |
|
|
|
157 |
|
|
if(lowest_timeout <= 0) {
|
158 |
|
|
tv->tv_sec = 0;
|
159 |
|
|
tv->tv_usec = 0;
|
160 |
|
|
} else {
|
161 |
|
|
tv->tv_sec = lowest_timeout / 1000;
|
162 |
|
|
tv->tv_usec = (lowest_timeout % 1000) * 1000;
|
163 |
|
|
}
|
164 |
|
|
|
165 |
|
|
return TRUE;
|
166 |
|
|
}
|
167 |
|
|
|
168 |
|
|
MWBOOL GdTimeout(void)
|
169 |
|
|
{
|
170 |
|
|
MWTIMER *n, *t = timerlist;
|
171 |
|
|
|
172 |
|
|
gettimeofday(¤t_time, NULL);
|
173 |
|
|
|
174 |
|
|
while(t) {
|
175 |
|
|
n = t->next;
|
176 |
|
|
if(time_to_expiry(&t->timeout) <= 0) {
|
177 |
|
|
t->callback(t->arg);
|
178 |
|
|
if (t->type == MWTIMER_ONESHOT)
|
179 |
|
|
{
|
180 |
|
|
/* One shot timer, is finished delete it now */
|
181 |
|
|
GdDestroyTimer(t);
|
182 |
|
|
}
|
183 |
|
|
else
|
184 |
|
|
{
|
185 |
|
|
/* Periodic timer needs to be reset */
|
186 |
|
|
calculate_timeval (&t->timeout, t->period);
|
187 |
|
|
}
|
188 |
|
|
}
|
189 |
|
|
t = n;
|
190 |
|
|
}
|
191 |
|
|
|
192 |
|
|
if(mainloop_timeout.tv_sec > 0 || mainloop_timeout.tv_usec > 0)
|
193 |
|
|
if(time_to_expiry(&mainloop_timeout) <= 0)
|
194 |
|
|
return TRUE;
|
195 |
|
|
|
196 |
|
|
return FALSE;
|
197 |
|
|
}
|
198 |
|
|
|
199 |
|
|
static void calculate_timeval(struct timeval *tv, MWTIMEOUT to)
|
200 |
|
|
{
|
201 |
|
|
tv->tv_sec = current_time.tv_sec + (to / 1000);
|
202 |
|
|
tv->tv_usec = current_time.tv_usec + ((to % 1000) * 1000);
|
203 |
|
|
if(tv->tv_usec > 1000000) {
|
204 |
|
|
tv->tv_sec++;
|
205 |
|
|
tv->tv_usec -= 1000000;
|
206 |
|
|
}
|
207 |
|
|
}
|
208 |
|
|
|
209 |
|
|
static signed long time_to_expiry(struct timeval *t)
|
210 |
|
|
{
|
211 |
|
|
MWTIMEOUT ret = (((t->tv_sec - current_time.tv_sec) * 1000) +
|
212 |
|
|
((t->tv_usec - current_time.tv_usec) / 1000));
|
213 |
|
|
|
214 |
|
|
return ret;
|
215 |
|
|
}
|