comparison packages/services/gfx/mw/current/src/engine/devtimer.c @ 208:e0c0827131d1 ecos

Merge from eCos master repository on 2002-05-20-20:11:54-BST
author jlarmour
date Mon, 20 May 2002 22:19:26 +0000
parents
children
comparison
equal deleted inserted replaced
207:74c807ddde34 208:e0c0827131d1
1 /*
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(&current_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 (&current_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(&current_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(&current_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 }