466292d93ce03f167a13f0cb982a496f5d660092
[profile/ivi/ecore.git] / src / lib / ecore / ecore_timer.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #include <stdlib.h>
6 #include <stdio.h>
7
8 #include "Ecore.h"
9 #include "ecore_private.h"
10
11
12 struct _Ecore_Timer
13 {
14    EINA_INLIST;
15    ECORE_MAGIC;
16    double          in;
17    double          at;
18    double          pending;
19    Ecore_Task_Cb   func;
20    void           *data;
21
22    int             references;
23    unsigned char   delete_me : 1;
24    unsigned char   just_added : 1;
25    unsigned char   frozen : 1;
26 };
27
28
29 static void _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data);
30
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;
38
39 /**
40  * @defgroup Ecore_Time_Group Ecore Time Functions
41  *
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.
44  */
45
46 /**
47  * Retrieves the current precision used by timer infrastructure.
48  *
49  * @see ecore_timer_precision_set()
50  */
51 EAPI double
52 ecore_timer_precision_get(void)
53 {
54    return precision;
55 }
56
57 /**
58  * Sets the precision to be used by timer infrastructure.
59  *
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
63  * able to save power.
64  *
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.
68  *
69  * Example:
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.
74  *
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.
78  *
79  * @param value allowed introduced timeout delay, in seconds.
80  */
81 EAPI void
82 ecore_timer_precision_set(double value)
83 {
84    if (value < 0.0)
85      {
86         ERR("Precision %f less than zero, ignored", value);
87         return;
88      }
89    precision = value;
90 }
91
92 /**
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
100  *
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.
104  *
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
109  * invalid.
110  */
111 EAPI Ecore_Timer *
112 ecore_timer_add(double in, Ecore_Task_Cb func, const void *data)
113 {
114    double now;
115    Ecore_Timer *timer;
116
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);
124    return timer;
125 }
126
127 /**
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
135  *
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.
139  */
140 EAPI Ecore_Timer *
141 ecore_timer_loop_add(double in, Ecore_Task_Cb func, const void *data)
142 {
143    double now;
144    Ecore_Timer *timer;
145
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);
153    return timer;
154 }
155
156 /**
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
162  *
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).
165  */
166 EAPI void *
167 ecore_timer_del(Ecore_Timer *timer)
168 {
169    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
170      {
171         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
172                          "ecore_timer_del");
173         return NULL;
174      }
175
176    if (timer->frozen && !timer->references)
177      {
178         void *data = timer->data;
179
180         suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
181
182         if (timer->delete_me)
183           timers_delete_me--;
184
185         free(timer);
186         return data;
187      }
188
189    EINA_SAFETY_ON_TRUE_RETURN_VAL(timer->delete_me, NULL);
190    timer->delete_me = 1;
191    timers_delete_me++;
192    return timer->data;
193 }
194
195 /**
196  * Change the interval the timer ticks of. If set during
197  * a timer call, this will affect the next interval.
198  *
199  * @param   timer The timer to change.
200  * @param   in    The interval in seconds.
201  * @ingroup Ecore_Time_Group
202  */
203 EAPI void
204 ecore_timer_interval_set(Ecore_Timer *timer, double in)
205 {
206    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
207      {
208         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
209                          "ecore_timer_interval_set");
210         return;
211      }
212    timer->in = in;
213 }
214
215 /**
216  * Get the interval the timer ticks on.
217  *
218  * @param   timer The timer to retrieve the interval from
219  * @return  The interval on success. -1 on failure.
220  * @ingroup Ecore_Time_Group
221  */
222 EAPI double
223 ecore_timer_interval_get(Ecore_Timer *timer)
224 {
225    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
226      {
227         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
228                          "ecore_timer_interval_get");
229         return -1.0;
230      }
231
232    return timer->in;
233 }
234
235 /**
236  * Add some delay for the next occurence of a timer.
237  * This doesn't affect the interval of a timer.
238  *
239  * @param   timer The timer to change.
240  * @param   add   The dalay to add to the next iteration.
241  * @ingroup Ecore_Time_Group
242  */
243 EAPI void
244 ecore_timer_delay(Ecore_Timer *timer, double add)
245 {
246    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
247      {
248         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
249                          "ecore_timer_delay");
250         return;
251      }
252
253    if (timer->frozen)
254      {
255         timer->pending += add;
256      }
257    else
258      {
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);
261      }
262 }
263
264 /**
265  * Get the pending time regarding a timer.
266  *
267  * @param       timer The timer to learn from.
268  * @ingroup     Ecore_Time_Group
269  */
270 EAPI double
271 ecore_timer_pending_get(Ecore_Timer *timer)
272 {
273    double       now;
274
275    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
276      {
277         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
278                          "ecore_timer_pending_get");
279         return 0;
280      }
281
282    now = ecore_time_get();
283
284    if (timer->frozen)
285      return timer->pending;
286    return timer->at - now;
287 }
288
289 /**
290  *
291  *
292  */
293 EAPI void
294 ecore_timer_freeze(Ecore_Timer *timer)
295 {
296    double now;
297
298    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
299      {
300         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
301                          "ecore_timer_freeze");
302         return ;
303      }
304
305    /* Timer already frozen */
306    if (timer->frozen)
307      return ;
308
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));
311
312    now = ecore_time_get();
313
314    timer->pending = timer->at - now;
315    timer->at = 0.0;
316    timer->frozen = 1;
317 }
318
319 EAPI void
320 ecore_timer_thaw(Ecore_Timer *timer)
321 {
322    double now;
323
324    if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER))
325      {
326         ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER,
327                          "ecore_timer_thaw");
328         return ;
329      }
330
331    /* Timer not frozen */
332    if (!timer->frozen)
333      return ;
334
335    suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
336    now = ecore_time_get();
337
338    _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data);
339 }
340
341 void
342 _ecore_timer_shutdown(void)
343 {
344    Ecore_Timer *timer;
345
346    while ((timer = timers))
347      {
348         timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers));
349         ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
350         free(timer);
351      }
352
353    while ((timer = suspended))
354      {
355         suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended));
356         ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
357         free(timer);
358      }
359
360    timer_current = NULL;
361 }
362
363 void
364 _ecore_timer_cleanup(void)
365 {
366    Ecore_Timer *l;
367    int in_use = 0, todo = timers_delete_me, done = 0;
368
369    if (!timers_delete_me) return;
370    for (l = timers; l;)
371      {
372         Ecore_Timer *timer = l;
373
374         l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
375         if (timer->delete_me)
376           {
377              if (timer->references)
378                {
379                   in_use++;
380                   continue;
381                }
382              timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
383              ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
384              free(timer);
385              timers_delete_me--;
386              done++;
387              if (timers_delete_me == 0) return;
388           }
389      }
390    for (l = suspended; l;)
391      {
392         Ecore_Timer *timer = l;
393
394         l = (Ecore_Timer *) EINA_INLIST_GET(l)->next;
395         if (timer->delete_me)
396           {
397              if (timer->references)
398                {
399                   in_use++;
400                   continue;
401                }
402              suspended = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer));
403              ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE);
404              free(timer);
405              timers_delete_me--;
406              done++;
407              if (timers_delete_me == 0) return;
408           }
409      }
410
411    if ((!in_use) && (timers_delete_me))
412      {
413         ERR("%d timers to delete, but they were not found!"
414             "Stats: todo=%d, done=%d, pending=%d, in_use=%d. "
415             "reset counter.",
416             timers_delete_me, todo, done, todo - done, in_use);
417         timers_delete_me = 0;
418      }
419 }
420
421 void
422 _ecore_timer_enable_new(void)
423 {
424    Ecore_Timer *timer;
425
426    if (!timers_added) return;
427    timers_added = 0;
428    EINA_INLIST_FOREACH(timers, timer) timer->just_added = 0;
429 }
430
431 int
432 _ecore_timers_exists(void)
433 {
434    Ecore_Timer *timer = timers;
435
436    while ((timer) && (timer->delete_me))
437      timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next;
438
439    return !!timer;
440 }
441
442 static inline Ecore_Timer *
443 _ecore_timer_first_get(void)
444 {
445    Ecore_Timer *timer = timers;
446
447    while ((timer) && ((timer->delete_me) || (timer->just_added)))
448      timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
449
450    return timer;
451 }
452
453 static inline Ecore_Timer *
454 _ecore_timer_after_get(Ecore_Timer *base)
455 {
456    Ecore_Timer *timer = (Ecore_Timer *) EINA_INLIST_GET(base)->next;
457    double maxtime = base->at + precision;
458
459    while ((timer) && ((timer->delete_me) || (timer->just_added)) && (timer->at <= maxtime))
460      timer = (Ecore_Timer *) EINA_INLIST_GET(timer)->next;
461
462    if ((!timer) || (timer->at > maxtime))
463      return NULL;
464
465    return timer;
466 }
467
468 double
469 _ecore_timer_next_get(void)
470 {
471    double now;
472    double in;
473    Ecore_Timer *first, *second;
474
475    first = _ecore_timer_first_get();
476    if (!first) return -1;
477
478    second = _ecore_timer_after_get(first);
479    if (second)
480      first = second;
481
482    now = ecore_loop_time_get();
483    in = first->at - now;
484    if (in < 0) in = 0;
485    return in;
486 }
487
488 static inline void
489 _ecore_timer_reschedule(Ecore_Timer *timer, double when)
490 {
491    if ((timer->delete_me) || (timer->frozen)) return;
492
493    timers = (Ecore_Timer *) eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
494
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.
503     */
504    if ((timer->at + timer->in) < (when - 15.0))
505      _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data);
506    else
507      _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data);
508 }
509
510 int
511 _ecore_timer_call(double when)
512 {
513    if (!timers) return 0;
514    if (last_check > when)
515      {
516         Ecore_Timer *timer;
517         /* User set time backwards */
518         EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when);
519      }
520    last_check = when;
521
522    if (!timer_current)
523      {
524         /* regular main loop, start from head */
525         timer_current = timers;
526      }
527    else
528      {
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);
533      }
534
535    while (timer_current)
536      {
537         Ecore_Timer *timer = timer_current;
538
539         if (timer->at > when)
540           {
541              timer_current = NULL; /* ended walk, next should restart. */
542              return 0;
543           }
544
545         if ((timer->just_added) || (timer->delete_me))
546           {
547              timer_current = (Ecore_Timer*)EINA_INLIST_GET(timer_current)->next;
548              continue;
549           }
550
551         timer->references++;
552         if (!timer->func(timer->data))
553           {
554              if (!timer->delete_me) ecore_timer_del(timer);
555           }
556         timer->references--;
557
558         if (timer_current) /* may have changed in recursive main loops */
559           timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next;
560
561         _ecore_timer_reschedule(timer, when);
562      }
563    return 0;
564 }
565
566 static void
567 _ecore_timer_set(Ecore_Timer *timer, double at, double in, Ecore_Task_Cb func, void *data)
568 {
569    Ecore_Timer *t2;
570
571    timers_added = 1;
572    timer->at = at;
573    timer->in = in;
574    timer->func = func;
575    timer->data = data;
576    timer->just_added = 1;
577    timer->frozen = 0;
578    timer->pending = 0.0;
579    if (timers)
580      {
581         EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2)
582           {
583              if (timer->at > t2->at)
584                {
585                   timers = (Ecore_Timer *) eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2));
586                   return;
587                }
588           }
589      }
590    timers = (Ecore_Timer *) eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer));
591 }