9 #include "ecore_private.h"
23 unsigned char delete_me : 1;
24 unsigned char just_added : 1;
25 unsigned char frozen : 1;
29 static void _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data);
31 static int timers_added = 0;
32 static int timers_delete_me = 0;
33 static Ecore_Timer *timers = NULL;
34 static Ecore_Timer *timer_current = NULL;
35 static Ecore_Timer *suspended = NULL;
36 static double last_check = 0.0;
37 static double precision = 10.0 / 1000000.0;
40 * @defgroup Ecore_Time_Group Ecore Time Functions
42 * Functions that deal with time. These functions include those that simply
43 * retrieve it in a given format, and those that create events based on it.
47 * Retrieves the current precision used by timer infrastructure.
49 * @see ecore_timer_precision_set()
52 ecore_timer_precision_get(void)
58 * Sets the precision to be used by timer infrastructure.
60 * When system calculates time to expire the next timer we'll be able
61 * to delay the timer by the given amount so more timers will fit in
62 * the same dispatch, waking up the system less often and thus being
65 * Be aware that kernel may delay delivery even further, these delays
66 * are always possible due other tasks having higher priorities or
67 * other scheduler policies.
70 * We have 2 timers, one that expires in a 2.0s and another that
71 * expires in 2.1s, if precision is 0.1s, then the Ecore will request
72 * for the next expire to happen in 2.1s and not 2.0s and another one
73 * of 0.1 as it would before.
75 * @note Ecore is smart enough to see if there are timers in the
76 * precision range, if it does not, in our example if no second timer
77 * in (T + precision) existed, then it would use the minimum timeout.
79 * @param value allowed introduced timeout delay, in seconds.
82 ecore_timer_precision_set(double value)
86 ERR("Precision %f less than zero, ignored", value);
93 * Creates a timer to call the given function in the given period of time.
94 * @param in The interval in seconds.
95 * @param func The given function. If @p func returns 1, the timer is
96 * rescheduled for the next interval @p in.
97 * @param data Data to pass to @p func when it is called.
98 * @return A timer object on success. @c NULL on failure.
99 * @ingroup Ecore_Time_Group
101 * This function adds a timer and returns its handle on success and NULL on
102 * failure. The function @p func will be called every @p in seconds. The
103 * function will be passed the @p data pointer as its parameter.
105 * When the timer @p func is called, it must return a value of either 1
106 * (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL).
107 * If it returns 1, it will be called again at the next tick, or if it returns
108 * 0 it will be deleted automatically making any references/handles for it
112 ecore_timer_add(double in, Ecore_Task_Cb func, const void *data)
117 if (!func) return NULL;
118 if (in < 0.0) in = 0.0;
119 timer = calloc(1, sizeof(Ecore_Timer));
120 if (!timer) return NULL;
121 ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
122 now = ecore_time_get();
123 _ecore_timer_set(timer, now + in, in, func, (void *)data);
128 * Creates a timer to call the given function in the given period of time.
129 * @param in The interval in seconds from current loop time.
130 * @param func The given function. If @p func returns 1, the timer is
131 * rescheduled for the next interval @p in.
132 * @param data Data to pass to @p func when it is called.
133 * @return A timer object on success. @c NULL on failure.
134 * @ingroup Ecore_Time_Group
136 * This is the same as ecore_timer_add(), but "now" is the time from
137 * ecore_loop_time_get() not ecore_time_get() as ecore_timer_add() uses. See
138 * ecore_timer_add() for more details.
141 ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data)
146 if (!func) return NULL;
147 if (in < 0.0) in = 0.0;
148 timer = calloc(1, sizeof(Ecore_Timer));
149 if (!timer) return NULL;
150 ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
151 now = ecore_loop_time_get();
152 _ecore_timer_set(timer, now + in, in, func, (void *)data);
157 * Delete the specified timer from the timer list.
158 * @param timer The timer to delete.
159 * @return The data pointer set for the timer when @ref ecore_timer_add was
160 * called. @c NULL is returned if the function is unsuccessful.
161 * @ingroup Ecore_Time_Group
163 * Note: @p timer must be a valid handle. If the timer function has already
164 * returned 0, the handle is no longer valid (and does not need to be delete).
167 ecore_timer_del(Ecore_Timer *timer)
169 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
171 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
176 if (timer->frozen && !timer->references)
178 void *data = timer->data;
180 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
182 if (timer->delete_me)
189 EINA_SAFETY_ON_TRUE_RETURN_VAL(timer->delete_me, NULL);
190 timer->delete_me = 1;
196 * Change the interval the timer ticks of. If set during
197 * a timer call, this will affect the next interval.
199 * @param timer The timer to change.
200 * @param in The interval in seconds.
201 * @ingroup Ecore_Time_Group
204 ecore_timer_interval_set(Ecore_Timer *timer, double in)
206 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
208 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
209 "ecore_timer_interval_set");
216 * Get the interval the timer ticks on.
218 * @param timer The timer to retrieve the interval from
219 * @return The interval on success. -1 on failure.
220 * @ingroup Ecore_Time_Group
223 ecore_timer_interval_get(Ecore_Timer *timer)
225 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
227 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
228 "ecore_timer_interval_get");
236 * Add some delay for the next occurence of a timer.
237 * This doesn't affect the interval of a timer.
239 * @param timer The timer to change.
240 * @param add The dalay to add to the next iteration.
241 * @ingroup Ecore_Time_Group
244 ecore_timer_delay(Ecore_Timer *timer, double add)
246 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
248 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
249 "ecore_timer_delay");
255 timer->pending += add;
259 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
260 _ecore_timer_set(timer, timer->at + add, timer->in, timer->func, timer->data);
265 * Get the pending time regarding a timer.
267 * @param timer The timer to learn from.
268 * @ingroup Ecore_Time_Group
271 ecore_timer_pending_get(Ecore_Timer *timer)
275 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
277 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
278 "ecore_timer_pending_get");
282 now = ecore_time_get();
285 return timer->pending;
286 return timer->at - now;
294 ecore_timer_freeze(Ecore_Timer *timer)
298 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
300 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
301 "ecore_timer_freeze");
305 /* Timer already frozen */
309 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
310 suspended = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
312 now = ecore_time_get();
314 timer->pending = timer->at - now;
320 ecore_timer_thaw(Ecore_Timer *timer)
324 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
326 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
331 /* Timer not frozen */
335 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
336 now = ecore_time_get();
338 _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data);
342 _ecore_timer_shutdown(void)
346 while ((timer = timers))
348 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers));
349 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
353 while ((timer = suspended))
355 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended));
356 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
360 timer_current = NULL;
364 _ecore_timer_cleanup(void)
367 int in_use = 0, todo = timers_delete_me, done = 0;
369 if (!timers_delete_me) return;
372 Ecore_Timer *timer = l;
374 l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
375 if (timer->delete_me)
377 if (timer->references)
382 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
383 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
387 if (timers_delete_me == 0) return;
390 for (l = suspended; l;)
392 Ecore_Timer *timer = l;
394 l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
395 if (timer->delete_me)
397 if (timer->references)
402 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
403 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
407 if (timers_delete_me == 0) return;
411 if ((!in_use) && (timers_delete_me))
413 ERR("%d timers to delete, but they were not found!"
414 "Stats: todo=%d, done=%d, pending=%d, in_use=%d. "
416 timers_delete_me, todo, done, todo - done, in_use);
417 timers_delete_me = 0;
422 _ecore_timer_enable_new(void)
426 if (!timers_added) return;
428 EINA_INLIST_FOREACH(timers, timer) timer->just_added = 0;
432 _ecore_timers_exists(void)
434 Ecore_Timer *timer = timers;
436 while ((timer) && (timer->delete_me))
437 timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next;
442 static inline Ecore_Timer *
443 _ecore_timer_first_get(void)
445 Ecore_Timer *timer = timers;
447 while ((timer) && ((timer->delete_me) || (timer->just_added)))
448 timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
453 static inline Ecore_Timer *
454 _ecore_timer_after_get(Ecore_Timer *base)
456 Ecore_Timer *timer = (Ecore_Timer *) EINA_INLIST_GET(base)->next;
457 double maxtime = base->at + precision;
459 while ((timer) && ((timer->delete_me) || (timer->just_added)) && (timer->at <= maxtime))
460 timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
462 if ((!timer) || (timer->at > maxtime))
469 _ecore_timer_next_get(void)
473 Ecore_Timer *first, *second;
475 first = _ecore_timer_first_get();
476 if (!first) return -1;
478 second = _ecore_timer_after_get(first);
482 now = ecore_loop_time_get();
483 in = first->at - now;
489 _ecore_timer_reschedule(Ecore_Timer *timer, double when)
491 if ((timer->delete_me) || (timer->frozen)) return;
493 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
495 /* if the timer would have gone off more than 15 seconds ago,
496 * assume that the system hung and set the timer to go off
497 * timer->in from now. this handles system hangs, suspends
498 * and more, so ecore will only "replay" the timers while
499 * the system is suspended if it is suspended for less than
500 * 15 seconds (basically). this also handles if the process
501 * is stopped in a debugger or IO and other handling gets
502 * really slow within the main loop.
504 if ((timer->at + timer->in) < (when - 15.0))
505 _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data);
507 _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data);
511 _ecore_timer_call(double when)
513 if (!timers) return 0;
514 if (last_check > when)
517 /* User set time backwards */
518 EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when);
524 /* regular main loop, start from head */
525 timer_current = timers;
529 /* recursive main loop, continue from where we were */
530 Ecore_Timer *timer_old = timer_current;
531 timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
532 _ecore_timer_reschedule(timer_old, when);
535 while (timer_current)
537 Ecore_Timer *timer = timer_current;
539 if (timer->at > when)
541 timer_current = NULL; /* ended walk, next should restart. */
545 if ((timer->just_added) || (timer->delete_me))
547 timer_current = (Ecore_Timer*)EINA_INLIST_GET(timer_current)->next;
552 if (!timer->func(timer->data))
554 if (!timer->delete_me) ecore_timer_del(timer);
558 if (timer_current) /* may have changed in recursive main loops */
559 timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
561 _ecore_timer_reschedule(timer, when);
567 _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data)
576 timer->just_added = 1;
578 timer->pending = 0.0;
581 EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2)
583 if (timer->at > t2->at)
585 timers = (Ecore_Timer *) eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2));
590 timers = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));