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