2 * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
13 #include "ecore_private.h"
23 int (*func) (void *data);
27 unsigned char delete_me : 1;
28 unsigned char just_added : 1;
29 unsigned char frozen : 1;
33 static void _ecore_timer_set(Ecore_Timer *timer, double at, double in, int (*func) (void *data), void *data);
35 static int timers_added = 0;
36 static int timers_delete_me = 0;
37 static Ecore_Timer *timers = NULL;
38 static Ecore_Timer *timer_current = NULL;
39 static Ecore_Timer *suspended = NULL;
40 static double last_check = 0.0;
41 static double precision = 10.0 / 1000000.0;
44 * @defgroup Ecore_Time_Group Ecore Time Functions
46 * Functions that deal with time. These functions include those that simply
47 * retrieve it in a given format, and those that create events based on it.
51 * Retrieves the current precision used by timer infrastructure.
53 * @see ecore_timer_precision_set()
56 ecore_timer_precision_get(void)
62 * Sets the precision to be used by timer infrastructure.
64 * When system calculates time to expire the next timer we'll be able
65 * to delay the timer by the given amount so more timers will fit in
66 * the same dispatch, waking up the system less often and thus being
69 * Be aware that kernel may delay delivery even further, these delays
70 * are always possible due other tasks having higher priorities or
71 * other scheduler policies.
74 * We have 2 timers, one that expires in a 2.0s and another that
75 * expires in 2.1s, if precision is 0.1s, then the Ecore will request
76 * for the next expire to happen in 2.1s and not 2.0s and another one
77 * of 0.1 as it would before.
79 * @note Ecore is smart enough to see if there are timers in the
80 * precision range, if it does not, in our example if no second timer
81 * in (T + precision) existed, then it would use the minimum timeout.
83 * @param value allowed introduced timeout delay, in seconds.
86 ecore_timer_precision_set(double value)
90 ERR("Precision %f less than zero, ignored", value);
97 * Creates a timer to call the given function in the given period of time.
98 * @param in The interval in seconds.
99 * @param func The given function. If @p func returns 1, the timer is
100 * rescheduled for the next interval @p in.
101 * @param data Data to pass to @p func when it is called.
102 * @return A timer object on success. @c NULL on failure.
103 * @ingroup Ecore_Time_Group
105 * This function adds a timer and returns its handle on success and NULL on
106 * failure. The function @p func will be called every @p in seconds. The
107 * function will be passed the @p data pointer as its parameter.
109 * When the timer @p func is called, it must return a value of either 1
110 * (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL).
111 * If it returns 1, it will be called again at the next tick, or if it returns
112 * 0 it will be deleted automatically making any references/handles for it
116 ecore_timer_add(double in, int (*func) (void *data), const void *data)
121 if (!func) return NULL;
122 if (in < 0.0) in = 0.0;
123 timer = calloc(1, sizeof(Ecore_Timer));
124 if (!timer) return NULL;
125 ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
126 now = ecore_time_get();
127 _ecore_timer_set(timer, now + in, in, func, (void *)data);
132 * Creates a timer to call the given function in the given period of time.
133 * @param in The interval in seconds from current loop time.
134 * @param func The given function. If @p func returns 1, the timer is
135 * rescheduled for the next interval @p in.
136 * @param data Data to pass to @p func when it is called.
137 * @return A timer object on success. @c NULL on failure.
138 * @ingroup Ecore_Time_Group
140 * This is the same as ecore_timer_add(), but "now" is the time from
141 * ecore_loop_time_get() not ecore_time_get() as ecore_timer_add() uses. See
142 * ecore_timer_add() for more details.
145 ecore_timer_loop_add(double in, int (*func) (void *data), const void *data)
150 if (!func) return NULL;
151 if (in < 0.0) in = 0.0;
152 timer = calloc(1, sizeof(Ecore_Timer));
153 if (!timer) return NULL;
154 ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER);
155 now = ecore_loop_time_get();
156 _ecore_timer_set(timer, now + in, in, func, (void *)data);
161 * Delete the specified timer from the timer list.
162 * @param timer The timer to delete.
163 * @return The data pointer set for the timer when @ref ecore_timer_add was
164 * called. @c NULL is returned if the function is unsuccessful.
165 * @ingroup Ecore_Time_Group
167 * Note: @p timer must be a valid handle. If the timer function has already
168 * returned 0, the handle is no longer valid (and does not need to be delete).
171 ecore_timer_del(Ecore_Timer *timer)
173 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
175 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
180 if (timer->frozen && !timer->references)
182 void *data = timer->data;
184 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
186 if (timer->delete_me)
193 if (timer->delete_me) return timer->data;
195 timer->delete_me = 1;
200 * Change the interval the timer ticks of. If set during
201 * a timer call, this will affect the next interval.
203 * @param timer The timer to change.
204 * @param in The interval in seconds.
205 * @ingroup Ecore_Time_Group
208 ecore_timer_interval_set(Ecore_Timer *timer, double in)
210 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
212 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
213 "ecore_timer_interval_set");
220 * Get the interval the timer ticks on.
222 * @param timer The timer to retrieve the interval from
223 * @return The interval on success. -1 on failure.
224 * @ingroup Ecore_Time_Group
227 ecore_timer_interval_get(Ecore_Timer *timer)
229 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
231 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
232 "ecore_timer_interval_get");
240 * Add some delay for the next occurence of a timer.
241 * This doesn't affect the interval of a timer.
243 * @param timer The timer to change.
244 * @param add The dalay to add to the next iteration.
245 * @ingroup Ecore_Time_Group
248 ecore_timer_delay(Ecore_Timer *timer, double add)
250 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
252 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
253 "ecore_timer_delay");
259 timer->pending += add;
263 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
264 _ecore_timer_set(timer, timer->at + add, timer->in, timer->func, timer->data);
269 * Get the pending time regarding a timer.
271 * @param timer The timer to learn from.
272 * @ingroup Ecore_Time_Group
275 ecore_timer_pending_get(Ecore_Timer *timer)
279 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
281 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
282 "ecore_timer_pending_get");
286 now = ecore_time_get();
289 return timer->pending;
290 return timer->at - now;
298 ecore_timer_freeze(Ecore_Timer *timer)
302 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
304 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
305 "ecore_timer_freeze");
309 /* Timer already frozen */
313 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
314 suspended = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
316 now = ecore_time_get();
318 timer->pending = timer->at - now;
324 ecore_timer_thaw(Ecore_Timer *timer)
328 if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
330 ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
335 /* Timer not frozen */
339 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
340 now = ecore_time_get();
342 _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data);
346 _ecore_timer_shutdown(void)
350 while ((timer = timers))
352 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers));
353 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
357 while ((timer = suspended))
359 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended));
360 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
364 timer_current = NULL;
368 _ecore_timer_cleanup(void)
373 if (!timers_delete_me) return;
376 Ecore_Timer *timer = l;
378 l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
379 if (timer->delete_me)
381 if (timer->references)
386 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
387 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
390 if (timers_delete_me == 0) return;
393 for (l = suspended; l;)
395 Ecore_Timer *timer = l;
397 l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
398 if (timer->delete_me)
400 if (timer->references)
405 suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
406 ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
409 if (timers_delete_me == 0) return;
413 if ((!in_use) && (timers_delete_me))
415 ERR("%d timers to delete, but they were not found! reset counter.",
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_call(double when)
491 if (!timers) return 0;
492 if (last_check > when)
495 /* User set time backwards */
496 EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when);
502 /* regular main loop, start from head */
503 timer_current = timers;
507 /* recursive main loop, continue from where we were */
508 timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
511 while (timer_current)
513 Ecore_Timer *timer = timer_current;
515 if (timer->at > when)
517 timer_current = NULL; /* ended walk, next should restart. */
521 if ((timer->just_added) || (timer->delete_me))
523 timer_current = (Ecore_Timer*)EINA_INLIST_GET(timer_current)->next;
528 if (!timer->func(timer->data)) ecore_timer_del(timer);
531 if (timer_current) /* may have changed in recursive main loops */
532 timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
534 if ((!timer->delete_me) && (!timer->frozen))
536 timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
538 /* if the timer would have gone off more than 15 seconds ago,
539 * assume that the system hung and set the timer to go off
540 * timer->in from now. this handles system hangs, suspends
541 * and more, so ecore will only "replay" the timers while
542 * the system is suspended if it is suspended for less than
543 * 15 seconds (basically). this also handles if the process
544 * is stopped in a debugger or IO and other handling gets
545 * really slow within the main loop.
547 if ((timer->at + timer->in) < (when - 15.0))
548 _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data);
550 _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data);
557 _ecore_timer_set(Ecore_Timer *timer, double at, double in, int (*func) (void *data), void *data)
566 timer->just_added = 1;
568 timer->pending = 0.0;
571 EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2)
573 if (timer->at > t2->at)
575 timers = (Ecore_Timer *) eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2));
580 timers = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));