1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #include <stdlib.h>
6 #include <stdio.h>
7 
8 #include <Eo.h>
9 
10 #include "Ecore.h"
11 #include "ecore_private.h"
12 
13 #define MY_CLASS EFL_LOOP_TIMER_CLASS
14 #define MY_CLASS_NAME "Efl_Loop_Timer"
15 
16 #define ECORE_TIMER_CHECK(obj) if (!efl_isa((obj), MY_CLASS)) return
17 
18 #define EFL_LOOP_TIMER_DATA_GET(o, td) \
19    Efl_Loop_Timer_Data *td = efl_data_scope_safe_get(o, EFL_LOOP_TIMER_CLASS)
20 
21 #define EFL_LOOP_TIMER_DATA_GET_OR_RETURN(o, ptr, ...) \
22    EFL_LOOP_TIMER_DATA_GET(o, ptr);                    \
23    if (EINA_UNLIKELY(!ptr))                            \
24      {                                                 \
25         ERR("No data for timer %p", o);                \
26         return __VA_ARGS__;                            \
27      }
28 
29 struct _Ecore_Timer_Legacy
30 {
31    Ecore_Task_Cb func;
32 
33    const void *data;
34 
35    Eina_Bool inside_call : 1;
36    Eina_Bool delete_me   : 1;
37 };
38 typedef struct _Ecore_Timer_Legacy Ecore_Timer_Legacy;
39 struct _Efl_Loop_Timer_Data
40 {
41    EINA_INLIST;
42 
43    Eo            *object;
44    Eo            *loop;
45    Efl_Loop_Data *loop_data;
46    Ecore_Timer_Legacy *legacy;
47 
48    double     in;
49    double     at;
50    double     pending;
51 
52    int        listening;
53 
54    Eina_Bool  just_added  : 1;
55    Eina_Bool  frozen      : 1;
56    Eina_Bool  initialized : 1;
57    Eina_Bool  noparent    : 1;
58    Eina_Bool  constructed : 1;
59    Eina_Bool  finalized   : 1;
60 };
61 
62 static void _efl_loop_timer_util_delay(Efl_Loop_Timer_Data *timer, double add);
63 static void _efl_loop_timer_util_instanciate(Efl_Loop_Data *loop, Efl_Loop_Timer_Data *timer);
64 static void _efl_loop_timer_set(Efl_Loop_Timer_Data *timer, double at, double in);
65 
66 static double precision = 10.0 / 1000000.0;
67 
68 EAPI double
ecore_timer_precision_get(void)69 ecore_timer_precision_get(void)
70 {
71    EINA_MAIN_LOOP_CHECK_RETURN_VAL(0.0);
72    return precision;
73 }
74 
75 EAPI void
ecore_timer_precision_set(double value)76 ecore_timer_precision_set(double value)
77 {
78    EINA_MAIN_LOOP_CHECK_RETURN;
79    if (value < 0.0) value = 0.0;
80    precision = value;
81 }
82 
83 static void
_check_timer_event_catcher_add(void * data,const Efl_Event * event)84 _check_timer_event_catcher_add(void *data, const Efl_Event *event)
85 {
86    Efl_Loop_Timer_Data *timer = data;
87    const Efl_Callback_Array_Item_Full *array = event->info;
88    int i;
89 
90    for (i = 0; array[i].desc != NULL; i++)
91      {
92         if (array[i].desc == EFL_LOOP_TIMER_EVENT_TIMER_TICK)
93           {
94              if (timer->listening++ > 0) return;
95              if (timer->finalized)
96                _efl_loop_timer_util_instanciate(timer->loop_data, timer);
97              // No need to walk more than once per array as you can not del
98              // a partial array
99              return;
100           }
101      }
102 }
103 
104 static void
_check_timer_event_catcher_del(void * data,const Efl_Event * event)105 _check_timer_event_catcher_del(void *data, const Efl_Event *event)
106 {
107    Efl_Loop_Timer_Data *timer = data;
108    const Efl_Callback_Array_Item_Full *array = event->info;
109    int i;
110 
111    for (i = 0; array[i].desc != NULL; i++)
112      {
113         if (array[i].desc == EFL_LOOP_TIMER_EVENT_TIMER_TICK)
114           {
115              if ((--timer->listening) > 0) return;
116              _efl_loop_timer_util_instanciate(timer->loop_data, timer);
117              return;
118           }
119      }
120 }
121 
122 EFL_CALLBACKS_ARRAY_DEFINE(timer_watch,
123                           { EFL_EVENT_CALLBACK_ADD, _check_timer_event_catcher_add },
124                           { EFL_EVENT_CALLBACK_DEL, _check_timer_event_catcher_del });
125 
126 EOLIAN static Eo *
_efl_loop_timer_efl_object_constructor(Eo * obj,Efl_Loop_Timer_Data * timer)127 _efl_loop_timer_efl_object_constructor(Eo *obj, Efl_Loop_Timer_Data *timer)
128 {
129    efl_constructor(efl_super(obj, MY_CLASS));
130    efl_event_callback_array_add(obj, timer_watch(), timer);
131    efl_wref_add(obj, &timer->object);
132 
133    timer->in = -1.0;
134    timer->constructed = EINA_TRUE;
135    return obj;
136 }
137 
138 EOLIAN static Eo *
_efl_loop_timer_efl_object_finalize(Eo * obj,Efl_Loop_Timer_Data * pd)139 _efl_loop_timer_efl_object_finalize(Eo *obj, Efl_Loop_Timer_Data *pd)
140 {
141    pd->loop = efl_provider_find(obj, EFL_LOOP_CLASS);
142    pd->loop_data = efl_data_scope_get(pd->loop, EFL_LOOP_CLASS);
143 
144    if (pd->at < efl_loop_time_get(pd->loop))
145      pd->at = ecore_time_get() + pd->in;
146    else pd->at += pd->in;
147 
148    if (pd->in < 0.0)
149      {
150         ERR("You need to specify the interval of a timer to create a valid timer.");
151         return NULL;
152      }
153    pd->initialized = EINA_TRUE;
154    pd->finalized = EINA_TRUE;
155    _efl_loop_timer_set(pd, pd->at, pd->in);
156    return efl_finalize(efl_super(obj, MY_CLASS));
157 }
158 
159 static void
_ecore_timer_legacy_del(void * data,const Efl_Event * event EINA_UNUSED)160 _ecore_timer_legacy_del(void *data, const Efl_Event *event EINA_UNUSED)
161 {
162    free(data);
163 }
164 
165 static void
_ecore_timer_legacy_tick(void * data,const Efl_Event * event)166 _ecore_timer_legacy_tick(void *data, const Efl_Event *event)
167 {
168    Ecore_Timer_Legacy *legacy = data;
169    Eina_Bool inside_call = legacy->inside_call;
170 
171    legacy->inside_call = 1;
172    if (!_ecore_call_task_cb(legacy->func, (void *)legacy->data) || legacy->delete_me)
173      {
174         legacy->delete_me = EINA_TRUE;
175         /* cannot destroy timer if recursing */
176         if (!inside_call)
177           efl_del(event->object);
178      }
179    /* only unset flag if not currently recursing */
180    else if (!inside_call)
181      legacy->inside_call = 0;
182 }
183 
184 EFL_CALLBACKS_ARRAY_DEFINE(legacy_timer,
185                           { EFL_LOOP_TIMER_EVENT_TIMER_TICK, _ecore_timer_legacy_tick },
186                           { EFL_EVENT_DEL, _ecore_timer_legacy_del });
187 
188 EAPI Ecore_Timer *
ecore_timer_add(double in,Ecore_Task_Cb func,const void * data)189 ecore_timer_add(double in, Ecore_Task_Cb func, const void *data)
190 {
191    Ecore_Timer_Legacy *legacy;
192    Eo *timer;
193 
194    EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
195    if (!func)
196      {
197         ERR("Callback function must be set up for the class.");
198         return NULL;
199      }
200    legacy = calloc(1, sizeof (Ecore_Timer_Legacy));
201    if (!legacy) return NULL;
202    legacy->func = func;
203    legacy->data = data;
204    timer = efl_add(MY_CLASS, efl_main_loop_get(),
205                   efl_event_callback_array_add(efl_added, legacy_timer(), legacy),
206                   efl_loop_timer_interval_set(efl_added, in));
207    EFL_LOOP_TIMER_DATA_GET_OR_RETURN(timer, td, NULL);
208    td->legacy = legacy;
209    return timer;
210 }
211 
212 EAPI Ecore_Timer *
ecore_timer_loop_add(double in,Ecore_Task_Cb func,const void * data)213 ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void  *data)
214 {
215    Ecore_Timer_Legacy *legacy;
216    Eo *timer;
217 
218    EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
219    if (!func)
220      {
221         ERR("Callback function must be set up for the class.");
222         return NULL;
223      }
224    legacy = calloc(1, sizeof (Ecore_Timer_Legacy));
225    if (!legacy) return NULL;
226    legacy->func = func;
227    legacy->data = data;
228    timer = efl_add(MY_CLASS, efl_main_loop_get(),
229                   efl_event_callback_array_add(efl_added, legacy_timer(), legacy),
230                   efl_loop_timer_loop_reset(efl_added),
231                   efl_loop_timer_interval_set(efl_added, in));
232    EFL_LOOP_TIMER_DATA_GET_OR_RETURN(timer, td, NULL);
233    td->legacy = legacy;
234    return timer;
235 }
236 
237 EAPI void *
ecore_timer_del(Ecore_Timer * timer)238 ecore_timer_del(Ecore_Timer *timer)
239 {
240    void *data;
241 
242    if (!timer) return NULL;
243    EINA_MAIN_LOOP_CHECK_RETURN_VAL(NULL);
244 
245    EFL_LOOP_TIMER_DATA_GET(timer, td);
246    // If legacy == NULL, this means double free or something
247    if ((!td) || (!td->legacy))
248      {
249         // Just in case it is an Eo timer, but not a legacy one.
250         ERR("You are trying to destroy a timer which seems dead already.");
251         efl_unref(timer);
252         return NULL;
253      }
254 
255    data = (void *)td->legacy->data;
256    if (td->legacy->inside_call) td->legacy->delete_me = EINA_TRUE;
257    else efl_del(timer);
258    return data;
259 }
260 
261 EOLIAN static void
_efl_loop_timer_timer_interval_set(Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer,double in)262 _efl_loop_timer_timer_interval_set(Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer, double in)
263 {
264    if (in < 0.0) in = 0.0;
265    timer->in = in;
266 }
267 
268 EOLIAN static double
_efl_loop_timer_timer_interval_get(const Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer)269 _efl_loop_timer_timer_interval_get(const Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer)
270 {
271    return timer->in;
272 }
273 
274 EOLIAN static void
_efl_loop_timer_timer_delay(Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * pd,double add)275 _efl_loop_timer_timer_delay(Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *pd, double add)
276 {
277    _efl_loop_timer_util_delay(pd, add);
278 }
279 
280 EOLIAN static void
_efl_loop_timer_timer_reset(Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer)281 _efl_loop_timer_timer_reset(Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer)
282 {
283    double now, add;
284 
285    if (!timer->loop_data) return;
286    // Do not reset the current timer while inside the callback
287    if (timer->loop_data->timer_current == timer) return;
288 
289    now = ecore_time_get();
290    if (!timer->initialized)
291      {
292         timer->at = now;
293         return;
294      }
295 
296    if (timer->frozen) add = timer->pending;
297    else add = timer->at - now;
298    _efl_loop_timer_util_delay(timer, timer->in - add);
299 }
300 
301 EOLIAN static void
_efl_loop_timer_timer_loop_reset(Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer)302 _efl_loop_timer_timer_loop_reset(Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer)
303 {
304    double now, add;
305 
306    if (!timer->loop_data) return;
307    // Do not reset the current timer while inside the callback
308    if (timer->loop_data->timer_current == timer) return;
309 
310    now = efl_loop_time_get(timer->loop);
311    if (!timer->initialized)
312      {
313         timer->at = now;
314         return;
315      }
316 
317    if (timer->frozen) add = timer->pending;
318    else add = timer->at - now;
319    _efl_loop_timer_util_delay(timer, timer->in - add);
320 }
321 
322 EOLIAN static double
_efl_loop_timer_time_pending_get(const Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer)323 _efl_loop_timer_time_pending_get(const Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer)
324 {
325    double now, ret = 0.0;
326 
327    now = ecore_time_get();
328    if (timer->frozen) ret = timer->pending;
329    else ret = timer->at - now;
330    return ret;
331 }
332 
333 EAPI void
ecore_timer_freeze(Ecore_Timer * timer)334 ecore_timer_freeze(Ecore_Timer *timer)
335 {
336    ECORE_TIMER_CHECK(timer);
337    efl_event_freeze(timer);
338 }
339 
340 EOLIAN static void
_efl_loop_timer_efl_object_event_freeze(Eo * obj,Efl_Loop_Timer_Data * timer)341 _efl_loop_timer_efl_object_event_freeze(Eo *obj, Efl_Loop_Timer_Data *timer)
342 {
343    double now = 0.0;
344 
345    efl_event_freeze(efl_super(obj, MY_CLASS));
346    // Timer already frozen
347    if (timer->frozen) return;
348 
349    /* not set if timer is not finalized */
350    if (timer->loop)
351      now = efl_loop_time_get(timer->loop);
352    /* only if timer interval has been set */
353    if (timer->initialized)
354      timer->pending = timer->at - now;
355    else
356      timer->pending = 0.0;
357    timer->at = 0.0;
358    timer->frozen = 1;
359 
360    _efl_loop_timer_util_instanciate(timer->loop_data, timer);
361 }
362 
363 EAPI Eina_Bool
ecore_timer_freeze_get(Ecore_Timer * timer)364 ecore_timer_freeze_get(Ecore_Timer *timer)
365 {
366    EINA_MAIN_LOOP_CHECK_RETURN_VAL(EINA_FALSE);
367    return !!efl_event_freeze_count_get(timer);
368 }
369 
370 EOLIAN static int
_efl_loop_timer_efl_object_event_freeze_count_get(const Eo * obj EINA_UNUSED,Efl_Loop_Timer_Data * timer)371 _efl_loop_timer_efl_object_event_freeze_count_get(const Eo *obj EINA_UNUSED, Efl_Loop_Timer_Data *timer)
372 {
373    return timer->frozen;
374 }
375 
376 EAPI void
ecore_timer_thaw(Ecore_Timer * timer)377 ecore_timer_thaw(Ecore_Timer *timer)
378 {
379    ECORE_TIMER_CHECK(timer);
380    efl_event_thaw(timer);
381 }
382 
383 EOLIAN static void
_efl_loop_timer_efl_object_event_thaw(Eo * obj,Efl_Loop_Timer_Data * timer)384 _efl_loop_timer_efl_object_event_thaw(Eo *obj, Efl_Loop_Timer_Data *timer)
385 {
386    double now;
387 
388    efl_event_thaw(efl_super(obj, MY_CLASS));
389 
390    if (!timer->frozen) return; // Timer not frozen
391    timer->frozen = 0;
392 
393    if (timer->loop_data)
394      {
395         timer->loop_data->suspended = eina_inlist_remove
396           (timer->loop_data->suspended, EINA_INLIST_GET(timer));
397      }
398    now = ecore_time_get();
399    _efl_loop_timer_set(timer, timer->pending + now, timer->in);
400 }
401 
402 EAPI char *
ecore_timer_dump(void)403 ecore_timer_dump(void)
404 {
405    return NULL;
406 }
407 
408 static void
_efl_loop_timer_util_loop_clear(Efl_Loop_Timer_Data * pd)409 _efl_loop_timer_util_loop_clear(Efl_Loop_Timer_Data *pd)
410 {
411    Eina_Inlist *first;
412 
413    if (!pd->loop_data) return;
414    // Check if we are the current timer, if so move along
415    if (pd->loop_data->timer_current == pd)
416      pd->loop_data->timer_current = (Efl_Loop_Timer_Data *)
417        EINA_INLIST_GET(pd)->next;
418 
419    // Remove the timer from all possible pending list
420    first = eina_inlist_first(EINA_INLIST_GET(pd));
421    if (first == pd->loop_data->timers)
422      pd->loop_data->timers = eina_inlist_remove
423        (pd->loop_data->timers, EINA_INLIST_GET(pd));
424    else if (first == pd->loop_data->suspended)
425      pd->loop_data->suspended = eina_inlist_remove
426        (pd->loop_data->suspended, EINA_INLIST_GET(pd));
427 }
428 
429 static void
_efl_loop_timer_util_instanciate(Efl_Loop_Data * loop,Efl_Loop_Timer_Data * timer)430 _efl_loop_timer_util_instanciate(Efl_Loop_Data *loop, Efl_Loop_Timer_Data *timer)
431 {
432    Efl_Loop_Timer_Data *t2;
433 
434    if (!loop) return;
435    _efl_loop_timer_util_loop_clear(timer);
436 
437    // And start putting it back where it belong
438    if ((!timer->listening) || (timer->frozen) ||
439        (timer->at <= 0.0) || (timer->in < 0.0))
440      {
441         loop->suspended = eina_inlist_prepend(loop->suspended,
442                                               EINA_INLIST_GET(timer));
443         return;
444      }
445 
446    if (!timer->initialized)
447      {
448         ERR("Trying to instantiate an uninitialized timer is impossible.");
449         return;
450      }
451 
452    EINA_INLIST_REVERSE_FOREACH(loop->timers, t2)
453      {
454         if (timer->at > t2->at)
455           {
456              loop->timers = eina_inlist_append_relative(loop->timers,
457                                                         EINA_INLIST_GET(timer),
458                                                         EINA_INLIST_GET(t2));
459              return;
460           }
461      }
462    loop->timers = eina_inlist_prepend(loop->timers, EINA_INLIST_GET(timer));
463 }
464 
465 static void
_efl_loop_timer_util_delay(Efl_Loop_Timer_Data * timer,double add)466 _efl_loop_timer_util_delay(Efl_Loop_Timer_Data *timer, double add)
467 {
468    if (!timer->initialized)
469      {
470         ERR("Impossible to delay an uninitialized timer.");
471         return;
472      }
473    if (timer->frozen)
474      {
475         timer->pending += add;
476         return;
477      }
478    _efl_loop_timer_set(timer, timer->at + add, timer->in);
479 }
480 
481 EOLIAN static void
_efl_loop_timer_efl_object_parent_set(Eo * obj,Efl_Loop_Timer_Data * pd,Efl_Object * parent)482 _efl_loop_timer_efl_object_parent_set(Eo *obj, Efl_Loop_Timer_Data *pd, Efl_Object *parent)
483 {
484    Eina_Inlist *first;
485 
486    efl_parent_set(efl_super(obj, EFL_LOOP_TIMER_CLASS), parent);
487 
488    if ((!pd->constructed) || (!pd->finalized)) return;
489 
490    // Remove the timer from all possible pending list
491    first = eina_inlist_first(EINA_INLIST_GET(pd));
492    if (first == pd->loop_data->timers)
493      {
494         /* if this timer is currently being processed, update the pointer here so it is not lost */
495         if (pd == pd->loop_data->timer_current)
496           pd->loop_data->timer_current = (Efl_Loop_Timer_Data*)EINA_INLIST_GET(pd)->next;
497         pd->loop_data->timers = eina_inlist_remove(pd->loop_data->timers, EINA_INLIST_GET(pd));
498      }
499    else if (first == pd->loop_data->suspended)
500      pd->loop_data->suspended = eina_inlist_remove
501        (pd->loop_data->suspended, EINA_INLIST_GET(pd));
502 
503    if (efl_invalidated_get(obj)) return;
504 
505    pd->loop = efl_provider_find(obj, EFL_LOOP_CLASS);
506    if (pd->loop)
507      pd->loop_data = efl_data_scope_get(pd->loop, EFL_LOOP_CLASS);
508    else
509      pd->loop_data = NULL;
510 
511    if (efl_parent_get(obj) != parent) return;
512 
513    _efl_loop_timer_util_instanciate(pd->loop_data, pd);
514 
515    if (parent != NULL) pd->noparent = EINA_FALSE;
516    else pd->noparent = EINA_TRUE;
517 }
518 
519 EOLIAN static void
_efl_loop_timer_efl_object_destructor(Eo * obj,Efl_Loop_Timer_Data * pd)520 _efl_loop_timer_efl_object_destructor(Eo *obj, Efl_Loop_Timer_Data *pd)
521 {
522    _efl_loop_timer_util_loop_clear(pd);
523    efl_destructor(efl_super(obj, MY_CLASS));
524 }
525 
526 void
_efl_loop_timer_enable_new(Eo * obj EINA_UNUSED,Efl_Loop_Data * pd)527 _efl_loop_timer_enable_new(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd)
528 {
529    Efl_Loop_Timer_Data *timer;
530 
531    if (!pd->timers_added) return;
532    pd->timers_added = 0;
533    EINA_INLIST_FOREACH(pd->timers, timer) timer->just_added = 0;
534 }
535 
536 int
_efl_loop_timers_exists(Eo * obj EINA_UNUSED,Efl_Loop_Data * pd)537 _efl_loop_timers_exists(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd)
538 {
539    return !!pd->timers;
540 }
541 
542 static inline Ecore_Timer *
_efl_loop_timer_first_get(Eo * ob EINA_UNUSED,Efl_Loop_Data * pd)543 _efl_loop_timer_first_get(Eo *ob EINA_UNUSED, Efl_Loop_Data *pd)
544 {
545    Efl_Loop_Timer_Data *timer;
546 
547    EINA_INLIST_FOREACH(pd->timers, timer)
548      {
549         if (!timer->just_added) return timer->object;
550      }
551    return NULL;
552 }
553 
554 static inline Efl_Loop_Timer_Data *
_efl_loop_timer_after_get(Efl_Loop_Timer_Data * base)555 _efl_loop_timer_after_get(Efl_Loop_Timer_Data *base)
556 {
557    Efl_Loop_Timer_Data *timer;
558    Efl_Loop_Timer_Data *valid_timer = base;
559    double maxtime = base->at + precision;
560 
561    EINA_INLIST_FOREACH(EINA_INLIST_GET(base)->next, timer)
562      {
563         if (EINA_UNLIKELY(!timer->initialized)) continue; // This shouldn't happen
564         if (timer->at >= maxtime) break;
565         if (!timer->just_added) valid_timer = timer;
566      }
567    return valid_timer;
568 }
569 
570 double
_efl_loop_timer_next_get(Eo * obj,Efl_Loop_Data * pd)571 _efl_loop_timer_next_get(Eo *obj, Efl_Loop_Data *pd)
572 {
573    Ecore_Timer *object;
574    Efl_Loop_Timer_Data *first;
575    double now;
576    double in;
577 
578    object = _efl_loop_timer_first_get(obj, pd);
579    if (!object) return -1;
580 
581    first = _efl_loop_timer_after_get(efl_data_scope_get(object, MY_CLASS));
582    now = efl_loop_time_get(obj);
583    in = first->at - now;
584    if (in < 0) in = 0;
585    return in;
586 }
587 
588 static inline void
_efl_loop_timer_reschedule(Efl_Loop_Timer_Data * timer,double when)589 _efl_loop_timer_reschedule(Efl_Loop_Timer_Data *timer, double when)
590 {
591    if (timer->frozen || efl_invalidated_get(timer->object) ||
592        (timer->legacy && timer->legacy->delete_me)) return;
593 
594    if (timer->loop_data &&
595        (EINA_INLIST_GET(timer)->next || EINA_INLIST_GET(timer)->prev))
596      {
597         if (timer->loop_data->timers && (!timer->noparent))
598           timer->loop_data->timers = eina_inlist_remove
599             (timer->loop_data->timers, EINA_INLIST_GET(timer));
600      }
601 
602    /* if the timer would have gone off more than 15 seconds ago,
603     * assume that the system hung and set the timer to go off
604     * timer->in from now. this handles system hangs, suspends
605     * and more, so ecore will only "replay" the timers while
606     * the system is suspended if it is suspended for less than
607     * 15 seconds (basically). this also handles if the process
608     * is stopped in a debugger or IO and other handling gets
609     * really slow within the main loop.
610     */
611    if ((timer->at + timer->in) < (when - 15.0))
612      _efl_loop_timer_set(timer, when + timer->in, timer->in);
613    else
614      _efl_loop_timer_set(timer, timer->at + timer->in, timer->in);
615 }
616 
617 void
_efl_loop_timer_expired_timers_call(Eo * obj,Efl_Loop_Data * pd,double when)618 _efl_loop_timer_expired_timers_call(Eo *obj, Efl_Loop_Data *pd, double when)
619 {
620    // call the first expired timer until no expired timers exist
621    while (_efl_loop_timer_expired_call(obj, pd, when));
622 }
623 
624 int
_efl_loop_timer_expired_call(Eo * obj EINA_UNUSED,Efl_Loop_Data * pd,double when)625 _efl_loop_timer_expired_call(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, double when)
626 {
627    if (!pd->timers) return 0;
628    if (pd->last_check > when)
629      {
630         Efl_Loop_Timer_Data *timer;
631         // User set time backwards
632         EINA_INLIST_FOREACH(pd->timers, timer)
633           timer->at -= (pd->last_check - when);
634      }
635    pd->last_check = when;
636 
637    if (!pd->timer_current) // regular main loop, start from head
638      pd->timer_current = (Efl_Loop_Timer_Data *)pd->timers;
639    else
640      {
641         // recursive main loop, continue from where we were
642         Efl_Loop_Timer_Data *timer_old = pd->timer_current;
643 
644         pd->timer_current = (Efl_Loop_Timer_Data *)
645           EINA_INLIST_GET(pd->timer_current)->next;
646         _efl_loop_timer_reschedule(timer_old, when);
647      }
648 
649    while (pd->timer_current)
650      {
651         Efl_Loop_Timer_Data *timer = pd->timer_current;
652 
653         if (timer->at > when)
654           {
655              pd->timer_current = NULL; // ended walk, next should restart.
656              return 0;
657           }
658 
659         if (timer->just_added)
660           {
661              pd->timer_current = (Efl_Loop_Timer_Data *)
662                EINA_INLIST_GET(pd->timer_current)->next;
663              continue;
664           }
665 
666         efl_ref(timer->object);
667         eina_evlog("+timer", timer, 0.0, NULL);
668         /* this can remove timer from its inlist in the legacy codepath */
669         efl_event_callback_call(timer->object, EFL_LOOP_TIMER_EVENT_TIMER_TICK, NULL);
670         eina_evlog("-timer", timer, 0.0, NULL);
671 
672         // may have changed in recursive main loops
673         // this current timer can not die yet as we hold a reference on it
674         /* this is tricky: the current timer cannot be deleted, but it CAN be removed from its inlist,
675          * thus breaking timer processing
676          */
677         if (pd->timer_current)
678           {
679              if (pd->timer_current == timer)
680                pd->timer_current = (Efl_Loop_Timer_Data *)EINA_INLIST_GET(pd->timer_current)->next;
681              /* assume this has otherwise been modified either due to recursive mainloop processing or
682               * the timer being removed from its inlist and carefully updating pd->timer_current in the
683               * process as only the most elite of engineers would think to do
684               */
685           }
686         _efl_loop_timer_reschedule(timer, when);
687         efl_unref(timer->object);
688      }
689    return 0;
690 }
691 
692 static void
_efl_loop_timer_set(Efl_Loop_Timer_Data * timer,double at,double in)693 _efl_loop_timer_set(Efl_Loop_Timer_Data *timer, double at, double in)
694 {
695    if (!timer->loop_data) return;
696    timer->loop_data->timers_added = 1;
697    timer->in = in;
698    timer->just_added = 1;
699    timer->initialized = 1;
700    if (!timer->frozen)
701      {
702         timer->at = at;
703         timer->pending = 0.0;
704      }
705    _efl_loop_timer_util_instanciate(timer->loop_data, timer);
706 }
707 
708 #include "efl_loop_timer.eo.c"
709 #include "efl_loop_timer_eo.legacy.c"
710