1 /* ______ ___ ___
2 * /\ _ \ /\_ \ /\_ \
3 * \ \ \L\ \\//\ \ \//\ \ __ __ _ __ ___
4 * \ \ __ \ \ \ \ \ \ \ /'__`\ /'_ `\/\`'__\/ __`\
5 * \ \ \/\ \ \_\ \_ \_\ \_/\ __//\ \L\ \ \ \//\ \L\ \
6 * \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
7 * \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
8 * /\____/
9 * \_/__/
10 *
11 * New timer API.
12 *
13 * By Peter Wang.
14 *
15 * See readme.txt for copyright information.
16 */
17
18
19 #include <stdlib.h>
20
21 #include "allegro5/allegro.h"
22 #include "allegro5/internal/aintern.h"
23 #include "allegro5/internal/aintern_dtor.h"
24 #include "allegro5/internal/aintern_events.h"
25 #include "allegro5/internal/aintern_exitfunc.h"
26 #include "allegro5/internal/aintern_system.h"
27 #include "allegro5/internal/aintern_timer.h"
28
29 #ifndef ALLEGRO_MSVC
30 #ifndef ALLEGRO_BCC32
31 #include <sys/time.h>
32 #endif
33 #endif
34
35
36 /* forward declarations */
37 static void timer_handle_tick(ALLEGRO_TIMER *timer);
38
39
40 struct ALLEGRO_TIMER
41 {
42 ALLEGRO_EVENT_SOURCE es;
43 bool started;
44 double speed_secs;
45 int64_t count;
46 double counter; /* counts down to zero=blastoff */
47 _AL_LIST_ITEM *dtor_item;
48 };
49
50
51
52 /*
53 * The timer thread that runs in the background to drive the timers.
54 */
55
56 static ALLEGRO_MUTEX *timers_mutex;
57 static _AL_VECTOR active_timers = _AL_VECTOR_INITIALIZER(ALLEGRO_TIMER *);
58 static _AL_THREAD * volatile timer_thread = NULL;
59 static ALLEGRO_COND *timer_cond = NULL;
60 static bool destroy_thread = false;
61
62
63 /* timer_thread_proc: [timer thread]
64 * The timer thread procedure itself.
65 */
timer_thread_proc(_AL_THREAD * self,void * unused)66 static void timer_thread_proc(_AL_THREAD *self, void *unused)
67 {
68 #if 0
69 /* Block all signals. */
70 /* This was needed for some reason in v4, but I can't remember why,
71 * and it might not be relevant now. It has a tendency to create
72 * zombie processes if the program is aborted abnormally, so I'm
73 * taking it out for now.
74 */
75 {
76 sigset_t mask;
77 sigfillset(&mask);
78 pthread_sigmask(SIG_BLOCK, &mask, NULL);
79 }
80 #endif
81
82 #ifdef ALLEGRO_QNX
83 /* thread priority adjustment for QNX:
84 * The timer thread is set to the highest relative priority.
85 * (see the comment in src/qnx/qsystem.c about the scheduling policy)
86 */
87 {
88 struct sched_param sparam;
89 int spolicy;
90
91 if (pthread_getschedparam(pthread_self(), &spolicy, &sparam) == EOK) {
92 sparam.sched_priority += 4;
93 pthread_setschedparam(pthread_self(), spolicy, &sparam);
94 }
95 }
96 #endif
97
98 double old_time = al_get_time();
99 double new_time;
100 double interval = 0.032768;
101
102 while (!_al_get_thread_should_stop(self)) {
103 al_lock_mutex(timers_mutex);
104 while (_al_vector_size(&active_timers) == 0 && !destroy_thread) {
105 al_wait_cond(timer_cond, timers_mutex);
106 old_time = al_get_time() - interval;
107 }
108 al_unlock_mutex(timers_mutex);
109
110 al_rest(interval);
111
112 al_lock_mutex(timers_mutex);
113 {
114 /* Calculate actual time elapsed. */
115 new_time = al_get_time();
116 interval = new_time - old_time;
117 old_time = new_time;
118
119 /* Handle a tick. */
120 interval = _al_timer_thread_handle_tick(interval);
121 }
122 al_unlock_mutex(timers_mutex);
123 }
124
125 (void)unused;
126 }
127
128
129
130 /* timer_thread_handle_tick: [timer thread]
131 * Call handle_tick() method of every timer in active_timers, and
132 * returns the duration that the timer thread should try to sleep
133 * next time.
134 */
_al_timer_thread_handle_tick(double interval)135 double _al_timer_thread_handle_tick(double interval)
136 {
137 double new_delay = 0.032768;
138 unsigned int i;
139
140 for (i = 0; i < _al_vector_size(&active_timers); i++) {
141 ALLEGRO_TIMER **slot = _al_vector_ref(&active_timers, i);
142 ALLEGRO_TIMER *timer = *slot;
143
144 timer->counter -= interval;
145
146 while (timer->counter <= 0) {
147 timer_handle_tick(timer);
148 timer->counter += timer->speed_secs;
149 }
150
151 if ((timer->counter > 0) && (timer->counter < new_delay))
152 new_delay = timer->counter;
153 }
154
155 return new_delay;
156 }
157
158
159
shutdown_timers(void)160 static void shutdown_timers(void)
161 {
162 ASSERT(_al_vector_size(&active_timers) == 0);
163
164 _al_vector_free(&active_timers);
165
166 if (timer_thread != NULL) {
167 destroy_thread = true;
168 al_lock_mutex(timers_mutex);
169 al_signal_cond(timer_cond);
170 al_unlock_mutex(timers_mutex);
171 _al_thread_join(timer_thread);
172 }
173
174 al_free(timer_thread);
175
176 timer_thread = NULL;
177
178 al_destroy_mutex(timers_mutex);
179
180 al_destroy_cond(timer_cond);
181 }
182
183
184
185 // logic common to al_start_timer and al_resume_timer
186 // al_start_timer : passes reset_counter = true to start from the beginning
187 // al_resume_timer: passes reset_counter = false to preserve the previous time
enable_timer(ALLEGRO_TIMER * timer,bool reset_counter)188 static void enable_timer(ALLEGRO_TIMER *timer, bool reset_counter)
189 {
190 ASSERT(timer);
191 {
192 if (timer->started)
193 return;
194
195 al_lock_mutex(timers_mutex);
196 {
197 ALLEGRO_TIMER **slot;
198
199 timer->started = true;
200
201 if (reset_counter)
202 timer->counter = timer->speed_secs;
203
204 slot = _al_vector_alloc_back(&active_timers);
205 *slot = timer;
206
207 al_signal_cond(timer_cond);
208 }
209 al_unlock_mutex(timers_mutex);
210
211 if (timer_thread == NULL) {
212 destroy_thread = false;
213 timer_thread = al_malloc(sizeof(_AL_THREAD));
214 _al_thread_create(timer_thread, timer_thread_proc, NULL);
215 }
216 }
217 }
218
219
220
_al_init_timers(void)221 void _al_init_timers(void)
222 {
223 timers_mutex = al_create_mutex();
224 timer_cond = al_create_cond();
225 _al_add_exit_func(shutdown_timers, "shutdown_timers");
226 }
227
228
229
_al_get_active_timers_count(void)230 int _al_get_active_timers_count(void)
231 {
232 return _al_vector_size(&active_timers);
233 }
234
235
236 /*
237 * Timer objects
238 */
239
240
241 /* Function: al_create_timer
242 */
al_create_timer(double speed_secs)243 ALLEGRO_TIMER *al_create_timer(double speed_secs)
244 {
245 ASSERT(speed_secs > 0);
246 {
247 ALLEGRO_TIMER *timer = al_malloc(sizeof *timer);
248
249 ASSERT(timer);
250
251 if (timer) {
252 _al_event_source_init(&timer->es);
253 timer->started = false;
254 timer->count = 0;
255 timer->speed_secs = speed_secs;
256 timer->counter = 0;
257
258 timer->dtor_item = _al_register_destructor(_al_dtor_list, "timer", timer,
259 (void (*)(void *)) al_destroy_timer);
260 }
261
262 return timer;
263 }
264 }
265
266
267
268 /* Function: al_destroy_timer
269 */
al_destroy_timer(ALLEGRO_TIMER * timer)270 void al_destroy_timer(ALLEGRO_TIMER *timer)
271 {
272 if (timer) {
273 al_stop_timer(timer);
274
275 _al_unregister_destructor(_al_dtor_list, timer->dtor_item);
276
277 _al_event_source_free(&timer->es);
278 al_free(timer);
279 }
280 }
281
282
283
284 /* Function: al_start_timer
285 */
al_start_timer(ALLEGRO_TIMER * timer)286 void al_start_timer(ALLEGRO_TIMER *timer)
287 {
288 enable_timer(timer, true); // true to reset the counter
289 }
290
291
292
293 /* Function: al_resume_timer
294 */
al_resume_timer(ALLEGRO_TIMER * timer)295 void al_resume_timer(ALLEGRO_TIMER *timer)
296 {
297 enable_timer(timer, false); // false to preserve the counter
298 }
299
300
301
302 /* Function: al_stop_timer
303 */
al_stop_timer(ALLEGRO_TIMER * timer)304 void al_stop_timer(ALLEGRO_TIMER *timer)
305 {
306 ASSERT(timer);
307 {
308 if (!timer->started)
309 return;
310
311 al_lock_mutex(timers_mutex);
312 {
313 _al_vector_find_and_delete(&active_timers, &timer);
314 timer->started = false;
315 }
316 al_unlock_mutex(timers_mutex);
317 }
318 }
319
320
321
322 /* Function: al_get_timer_started
323 */
al_get_timer_started(const ALLEGRO_TIMER * timer)324 bool al_get_timer_started(const ALLEGRO_TIMER *timer)
325 {
326 ASSERT(timer);
327
328 return timer->started;
329 }
330
331
332
333 /* Function: al_get_timer_speed
334 */
al_get_timer_speed(const ALLEGRO_TIMER * timer)335 double al_get_timer_speed(const ALLEGRO_TIMER *timer)
336 {
337 ASSERT(timer);
338
339 return timer->speed_secs;
340 }
341
342
343
344 /* Function: al_set_timer_speed
345 */
al_set_timer_speed(ALLEGRO_TIMER * timer,double new_speed_secs)346 void al_set_timer_speed(ALLEGRO_TIMER *timer, double new_speed_secs)
347 {
348 ASSERT(timer);
349 ASSERT(new_speed_secs > 0);
350
351 al_lock_mutex(timers_mutex);
352 {
353 if (timer->started) {
354 timer->counter -= timer->speed_secs;
355 timer->counter += new_speed_secs;
356 }
357
358 timer->speed_secs = new_speed_secs;
359 }
360 al_unlock_mutex(timers_mutex);
361 }
362
363
364
365 /* Function: al_get_timer_count
366 */
al_get_timer_count(const ALLEGRO_TIMER * timer)367 int64_t al_get_timer_count(const ALLEGRO_TIMER *timer)
368 {
369 ASSERT(timer);
370
371 return timer->count;
372 }
373
374
375
376 /* Function: al_set_timer_count
377 */
al_set_timer_count(ALLEGRO_TIMER * timer,int64_t new_count)378 void al_set_timer_count(ALLEGRO_TIMER *timer, int64_t new_count)
379 {
380 ASSERT(timer);
381
382 al_lock_mutex(timers_mutex);
383 {
384 timer->count = new_count;
385 }
386 al_unlock_mutex(timers_mutex);
387 }
388
389
390
391 /* Function: al_add_timer_count
392 */
al_add_timer_count(ALLEGRO_TIMER * timer,int64_t diff)393 void al_add_timer_count(ALLEGRO_TIMER *timer, int64_t diff)
394 {
395 ASSERT(timer);
396
397 al_lock_mutex(timers_mutex);
398 {
399 timer->count += diff;
400 }
401 al_unlock_mutex(timers_mutex);
402 }
403
404
405 /* timer_handle_tick: [timer thread]
406 * Handle a single tick.
407 */
timer_handle_tick(ALLEGRO_TIMER * timer)408 static void timer_handle_tick(ALLEGRO_TIMER *timer)
409 {
410 /* Lock out event source helper functions (e.g. the release hook
411 * could be invoked simultaneously with this function).
412 */
413 _al_event_source_lock(&timer->es);
414 {
415 /* Update the count. */
416 timer->count++;
417
418 /* Generate an event, maybe. */
419 if (_al_event_source_needs_to_generate_event(&timer->es)) {
420 ALLEGRO_EVENT event;
421 event.timer.type = ALLEGRO_EVENT_TIMER;
422 event.timer.timestamp = al_get_time();
423 event.timer.count = timer->count;
424 event.timer.error = -timer->counter;
425 _al_event_source_emit_event(&timer->es, &event);
426 }
427 }
428 _al_event_source_unlock(&timer->es);
429 }
430
431
432
433 /* Function: al_get_timer_event_source
434 */
al_get_timer_event_source(ALLEGRO_TIMER * timer)435 ALLEGRO_EVENT_SOURCE *al_get_timer_event_source(ALLEGRO_TIMER *timer)
436 {
437 return &timer->es;
438 }
439
440
441 /*
442 * Local Variables:
443 * c-basic-offset: 3
444 * indent-tabs-mode: nil
445 * End:
446 */
447 /* vim: set sts=3 sw=3 et: */
448