1 /*
2 * Copyright (C) 2006-2019 Anders Gavare. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * Timer framework. This is used by emulated clocks.
29 */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <sys/time.h>
37
38 #include "misc.h"
39 #include "timer.h"
40
41
42 /* #define TEST */
43
44
45 struct timer {
46 struct timer *next;
47
48 double freq;
49 void (*timer_tick)(struct timer *timer, void *extra);
50 void *extra;
51
52 double interval;
53 double next_tick_at;
54 };
55
56 static struct timer *first_timer = NULL;
57 struct timeval timer_start_tv;
58 static double timer_freq;
59 static int timer_countdown_to_next_gettimeofday;
60 static double timer_current_time;
61 static double timer_current_time_step;
62
63 static int timer_is_running;
64
65 #define SECONDS_BETWEEN_GETTIMEOFDAY_SYNCH 1.65
66
67
68 /*
69 * timer_add():
70 *
71 * Adds a virtual timer to the list of timers.
72 *
73 * Return value is a pointer to a timer struct.
74 */
timer_add(double freq,void (* timer_tick)(struct timer * timer,void * extra),void * extra)75 struct timer *timer_add(double freq, void (*timer_tick)(struct timer *timer,
76 void *extra), void *extra)
77 {
78 struct timer *newtimer;
79
80 CHECK_ALLOCATION(newtimer = (struct timer *) malloc(sizeof(struct timer)));
81
82 if (freq <= 0.00000001)
83 freq = 0.00000001;
84
85 newtimer->freq = freq;
86 newtimer->timer_tick = timer_tick;
87 newtimer->extra = extra;
88
89 newtimer->interval = 1.0 / freq;
90 newtimer->next_tick_at = timer_current_time + newtimer->interval;
91
92 newtimer->next = first_timer;
93 first_timer = newtimer;
94
95 return newtimer;
96 }
97
98
99 /*
100 * timer_remove():
101 *
102 * Removes a virtual timer from the list of timers.
103 */
timer_remove(struct timer * t)104 void timer_remove(struct timer *t)
105 {
106 struct timer *prev = NULL, *cur = first_timer;
107
108 while (cur != NULL && cur != t) {
109 prev = cur;
110 cur = cur->next;
111 }
112
113 if (cur == t) {
114 if (prev == NULL)
115 first_timer = cur->next;
116 else
117 prev->next = cur->next;
118 free(cur);
119 } else {
120 fprintf(stderr, "attempt to remove timer %p which "
121 "doesn't exist. aborting\n", t);
122 exit(1);
123 }
124 }
125
126
127 /*
128 * timer_update_frequency():
129 *
130 * Changes the frequency of an existing timer.
131 */
timer_update_frequency(struct timer * t,double new_freq)132 void timer_update_frequency(struct timer *t, double new_freq)
133 {
134 if (t->freq == new_freq)
135 return;
136
137 t->freq = new_freq;
138
139 if (new_freq <= 0.00000001)
140 new_freq = 0.00000001;
141
142 t->interval = 1.0 / new_freq;
143 t->next_tick_at = timer_current_time + t->interval;
144 }
145
146
147 /*
148 * timer_tick():
149 *
150 * Timer tick handler. This is where the interesting stuff happens.
151 */
timer_tick(int signal_nr)152 static void timer_tick(int signal_nr)
153 {
154 struct timer *timer = first_timer;
155 struct timeval tv;
156
157 timer_current_time += timer_current_time_step;
158
159 if ((--timer_countdown_to_next_gettimeofday) < 0) {
160 gettimeofday(&tv, NULL);
161 tv.tv_sec -= timer_start_tv.tv_sec;
162 tv.tv_usec -= timer_start_tv.tv_usec;
163 if (tv.tv_usec < 0) {
164 tv.tv_usec += 1000000;
165 tv.tv_sec --;
166 }
167
168 #ifdef TIMER_DEBUG
169 /* For debugging/testing: */
170 {
171 double diff = tv.tv_usec * 0.000001 + tv.tv_sec
172 - timer_current_time;
173 printf("timer: lagging behind %f seconds\n", diff);
174 }
175 #endif
176
177 /* Get exponentially closer to the real time, instead of
178 just changing to it directly: */
179 timer_current_time = ( (tv.tv_usec * 0.000001 + tv.tv_sec) +
180 timer_current_time ) / 2;
181
182 timer_countdown_to_next_gettimeofday = (int64_t) (timer_freq *
183 SECONDS_BETWEEN_GETTIMEOFDAY_SYNCH);
184 }
185
186 while (timer != NULL) {
187 while (timer_current_time >= timer->next_tick_at) {
188 timer->timer_tick(timer, timer->extra);
189 timer->next_tick_at += timer->interval;
190 }
191
192 timer = timer->next;
193 }
194
195 #ifdef TEST
196 printf("T"); fflush(stdout);
197 #endif
198 }
199
200
201 /*
202 * timer_start():
203 *
204 * Set the interval timer to timer_freq Hz, and install the signal handler.
205 */
timer_start(void)206 void timer_start(void)
207 {
208 struct timer *timer = first_timer;
209 struct itimerval val;
210 struct sigaction saction;
211
212 if (timer_is_running)
213 return;
214
215 timer_is_running = 1;
216
217 gettimeofday(&timer_start_tv, NULL);
218 timer_current_time = 0.0;
219
220 /* Reset all timers: */
221 while (timer != NULL) {
222 timer->next_tick_at = timer->interval;
223 timer = timer->next;
224 }
225 val.it_interval.tv_sec = 0;
226 val.it_interval.tv_usec = (int) (1000000.0 / timer_freq);
227 val.it_value.tv_sec = 0;
228 val.it_value.tv_usec = (int) (1000000.0 / timer_freq);
229
230 memset(&saction, 0, sizeof(saction));
231 saction.sa_handler = timer_tick;
232 saction.sa_flags = SA_RESTART;
233
234 sigaction(SIGALRM, &saction, NULL);
235
236 setitimer(ITIMER_REAL, &val, NULL);
237 }
238
239
240 /*
241 * timer_stop():
242 *
243 * Deinstall the signal handler, and disable the interval timer.
244 */
timer_stop(void)245 void timer_stop(void)
246 {
247 struct itimerval val;
248 struct sigaction saction;
249
250 if (!timer_is_running)
251 return;
252
253 timer_is_running = 0;
254
255 val.it_interval.tv_sec = 0;
256 val.it_interval.tv_usec = 0;
257 val.it_value.tv_sec = 0;
258 val.it_value.tv_usec = 0;
259
260 setitimer(ITIMER_REAL, &val, NULL);
261
262 memset(&saction, 0, sizeof(saction));
263 saction.sa_handler = NULL;
264
265 sigaction(SIGALRM, &saction, NULL);
266 }
267
268
269 #ifdef TEST
timer_tick_test(struct timer * t,void * extra)270 static void timer_tick_test(struct timer *t, void *extra)
271 {
272 printf((char *) extra); fflush(stdout);
273 }
274 #endif
275
276
277 /*
278 * timer_init():
279 *
280 * Initialize the timer framework.
281 */
timer_init(void)282 void timer_init(void)
283 {
284 first_timer = NULL;
285 timer_current_time = 0.0;
286 timer_is_running = 0;
287 timer_countdown_to_next_gettimeofday = 0;
288
289 timer_freq = TIMER_BASE_FREQUENCY;
290 timer_current_time_step = 1.0 / timer_freq;
291
292 #ifdef TEST
293 timer_add(0.5, timer_tick_test, "X");
294 timer_add(10.0, timer_tick_test, ".");
295 timer_add(200.0, timer_tick_test, " ");
296 timer_start();
297 while (1)
298 sleep(999999);
299 #endif
300 }
301
302