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