diff options
Diffstat (limited to '')
-rw-r--r-- | libraries/ecore/src/lib/ecore/ecore_timer.c | 800 |
1 files changed, 800 insertions, 0 deletions
diff --git a/libraries/ecore/src/lib/ecore/ecore_timer.c b/libraries/ecore/src/lib/ecore/ecore_timer.c new file mode 100644 index 0000000..9c66545 --- /dev/null +++ b/libraries/ecore/src/lib/ecore/ecore_timer.c | |||
@@ -0,0 +1,800 @@ | |||
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 | #ifdef WANT_ECORE_TIMER_DUMP | ||
12 | # include <string.h> | ||
13 | # include <execinfo.h> | ||
14 | # define ECORE_TIMER_DEBUG_BT_NUM 64 | ||
15 | typedef void (*Ecore_Timer_Bt_Func)(); | ||
16 | #endif | ||
17 | |||
18 | struct _Ecore_Timer | ||
19 | { | ||
20 | EINA_INLIST; | ||
21 | ECORE_MAGIC; | ||
22 | double in; | ||
23 | double at; | ||
24 | double pending; | ||
25 | Ecore_Task_Cb func; | ||
26 | void *data; | ||
27 | |||
28 | #ifdef WANT_ECORE_TIMER_DUMP | ||
29 | Ecore_Timer_Bt_Func timer_bt[ECORE_TIMER_DEBUG_BT_NUM]; | ||
30 | int timer_bt_num; | ||
31 | #endif | ||
32 | |||
33 | int references; | ||
34 | unsigned char delete_me : 1; | ||
35 | unsigned char just_added : 1; | ||
36 | unsigned char frozen : 1; | ||
37 | }; | ||
38 | |||
39 | static void _ecore_timer_set(Ecore_Timer *timer, | ||
40 | double at, | ||
41 | double in, | ||
42 | Ecore_Task_Cb func, | ||
43 | void *data); | ||
44 | #ifdef WANT_ECORE_TIMER_DUMP | ||
45 | static int _ecore_timer_cmp(const void *d1, | ||
46 | const void *d2); | ||
47 | #endif | ||
48 | |||
49 | static int timers_added = 0; | ||
50 | static int timers_delete_me = 0; | ||
51 | static Ecore_Timer *timers = NULL; | ||
52 | static Ecore_Timer *timer_current = NULL; | ||
53 | static Ecore_Timer *suspended = NULL; | ||
54 | static double last_check = 0.0; | ||
55 | static double precision = 10.0 / 1000000.0; | ||
56 | |||
57 | /** | ||
58 | * @addtogroup Ecore_Time_Group | ||
59 | * | ||
60 | * @{ | ||
61 | */ | ||
62 | |||
63 | /** | ||
64 | * Retrieves the current precision used by timer infrastructure. | ||
65 | * | ||
66 | * @see ecore_timer_precision_set() | ||
67 | */ | ||
68 | EAPI double | ||
69 | ecore_timer_precision_get(void) | ||
70 | { | ||
71 | return precision; | ||
72 | } | ||
73 | |||
74 | /** | ||
75 | * Sets the precision to be used by timer infrastructure. | ||
76 | * | ||
77 | * When system calculates time to expire the next timer we'll be able | ||
78 | * to delay the timer by the given amount so more timers will fit in | ||
79 | * the same dispatch, waking up the system less often and thus being | ||
80 | * able to save power. | ||
81 | * | ||
82 | * Be aware that kernel may delay delivery even further, these delays | ||
83 | * are always possible due other tasks having higher priorities or | ||
84 | * other scheduler policies. | ||
85 | * | ||
86 | * Example: | ||
87 | * We have 2 timers, one that expires in a 2.0s and another that | ||
88 | * expires in 2.1s, if precision is 0.1s, then the Ecore will request | ||
89 | * for the next expire to happen in 2.1s and not 2.0s and another one | ||
90 | * of 0.1 as it would before. | ||
91 | * | ||
92 | * @note Ecore is smart enough to see if there are timers in the | ||
93 | * precision range, if it does not, in our example if no second timer | ||
94 | * in (T + precision) existed, then it would use the minimum timeout. | ||
95 | * | ||
96 | * @param value allowed introduced timeout delay, in seconds. | ||
97 | */ | ||
98 | EAPI void | ||
99 | ecore_timer_precision_set(double value) | ||
100 | { | ||
101 | _ecore_lock(); | ||
102 | |||
103 | if (value < 0.0) | ||
104 | { | ||
105 | ERR("Precision %f less than zero, ignored", value); | ||
106 | goto unlock; | ||
107 | } | ||
108 | precision = value; | ||
109 | |||
110 | unlock: | ||
111 | _ecore_unlock(); | ||
112 | } | ||
113 | |||
114 | /** | ||
115 | * Creates a timer to call the given function in the given period of time. | ||
116 | * @param in The interval in seconds. | ||
117 | * @param func The given function. If @p func returns 1, the timer is | ||
118 | * rescheduled for the next interval @p in. | ||
119 | * @param data Data to pass to @p func when it is called. | ||
120 | * @return A timer object on success. @c NULL on failure. | ||
121 | * | ||
122 | * This function adds a timer and returns its handle on success and NULL on | ||
123 | * failure. The function @p func will be called every @p in seconds. The | ||
124 | * function will be passed the @p data pointer as its parameter. | ||
125 | * | ||
126 | * When the timer @p func is called, it must return a value of either 1 | ||
127 | * (or ECORE_CALLBACK_RENEW) or 0 (or ECORE_CALLBACK_CANCEL). | ||
128 | * If it returns 1, it will be called again at the next tick, or if it returns | ||
129 | * 0 it will be deleted automatically making any references/handles for it | ||
130 | * invalid. | ||
131 | */ | ||
132 | EAPI Ecore_Timer * | ||
133 | ecore_timer_add(double in, | ||
134 | Ecore_Task_Cb func, | ||
135 | const void *data) | ||
136 | { | ||
137 | double now; | ||
138 | Ecore_Timer *timer = NULL; | ||
139 | |||
140 | _ecore_lock(); | ||
141 | if (!func) goto unlock; | ||
142 | if (in < 0.0) in = 0.0; | ||
143 | timer = calloc(1, sizeof(Ecore_Timer)); | ||
144 | if (!timer) goto unlock; | ||
145 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER); | ||
146 | now = ecore_time_get(); | ||
147 | |||
148 | #ifdef WANT_ECORE_TIMER_DUMP | ||
149 | timer->timer_bt_num = backtrace((void **)(timer->timer_bt), | ||
150 | ECORE_TIMER_DEBUG_BT_NUM); | ||
151 | #endif | ||
152 | |||
153 | _ecore_timer_set(timer, now + in, in, func, (void *)data); | ||
154 | unlock: | ||
155 | _ecore_unlock(); | ||
156 | return timer; | ||
157 | } | ||
158 | |||
159 | /** | ||
160 | * Creates a timer to call the given function in the given period of time. | ||
161 | * @param in The interval in seconds from current loop time. | ||
162 | * @param func The given function. If @p func returns 1, the timer is | ||
163 | * rescheduled for the next interval @p in. | ||
164 | * @param data Data to pass to @p func when it is called. | ||
165 | * @return A timer object on success. @c NULL on failure. | ||
166 | * | ||
167 | * This is the same as ecore_timer_add(), but "now" is the time from | ||
168 | * ecore_loop_time_get() not ecore_time_get() as ecore_timer_add() uses. See | ||
169 | * ecore_timer_add() for more details. | ||
170 | */ | ||
171 | EAPI Ecore_Timer * | ||
172 | ecore_timer_loop_add(double in, | ||
173 | Ecore_Task_Cb func, | ||
174 | const void *data) | ||
175 | { | ||
176 | Ecore_Timer *timer; | ||
177 | |||
178 | _ecore_lock(); | ||
179 | timer = _ecore_timer_loop_add(in, func, data); | ||
180 | _ecore_unlock(); | ||
181 | |||
182 | return timer; | ||
183 | } | ||
184 | |||
185 | /** | ||
186 | * Delete the specified timer from the timer list. | ||
187 | * @param timer The timer to delete. | ||
188 | * @return The data pointer set for the timer when @ref ecore_timer_add was | ||
189 | * called. @c NULL is returned if the function is unsuccessful. | ||
190 | * | ||
191 | * Note: @p timer must be a valid handle. If the timer function has already | ||
192 | * returned 0, the handle is no longer valid (and does not need to be delete). | ||
193 | */ | ||
194 | EAPI void * | ||
195 | ecore_timer_del(Ecore_Timer *timer) | ||
196 | { | ||
197 | void *data = NULL; | ||
198 | |||
199 | _ecore_lock(); | ||
200 | |||
201 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
202 | { | ||
203 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
204 | "ecore_timer_del"); | ||
205 | goto unlock; | ||
206 | } | ||
207 | |||
208 | data = _ecore_timer_del(timer); | ||
209 | |||
210 | unlock: | ||
211 | _ecore_unlock(); | ||
212 | return data; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Change the interval the timer ticks of. If set during | ||
217 | * a timer call, this will affect the next interval. | ||
218 | * | ||
219 | * @param timer The timer to change. | ||
220 | * @param in The interval in seconds. | ||
221 | */ | ||
222 | EAPI void | ||
223 | ecore_timer_interval_set(Ecore_Timer *timer, | ||
224 | double in) | ||
225 | { | ||
226 | _ecore_lock(); | ||
227 | |||
228 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
229 | { | ||
230 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
231 | "ecore_timer_interval_set"); | ||
232 | goto unlock; | ||
233 | } | ||
234 | timer->in = in; | ||
235 | unlock: | ||
236 | _ecore_unlock(); | ||
237 | } | ||
238 | |||
239 | /** | ||
240 | * Get the interval the timer ticks on. | ||
241 | * | ||
242 | * @param timer The timer to retrieve the interval from | ||
243 | * @return The interval on success. -1 on failure. | ||
244 | */ | ||
245 | EAPI double | ||
246 | ecore_timer_interval_get(Ecore_Timer *timer) | ||
247 | { | ||
248 | double interval; | ||
249 | |||
250 | _ecore_lock(); | ||
251 | |||
252 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
253 | { | ||
254 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
255 | "ecore_timer_interval_get"); | ||
256 | interval = -1.0; | ||
257 | goto unlock; | ||
258 | } | ||
259 | |||
260 | interval = timer->in; | ||
261 | unlock: | ||
262 | _ecore_unlock(); | ||
263 | return interval; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * Add some delay for the next occurrence of a timer. | ||
268 | * This doesn't affect the interval of a timer. | ||
269 | * | ||
270 | * @param timer The timer to change. | ||
271 | * @param add The dalay to add to the next iteration. | ||
272 | */ | ||
273 | EAPI void | ||
274 | ecore_timer_delay(Ecore_Timer *timer, | ||
275 | double add) | ||
276 | { | ||
277 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
278 | { | ||
279 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
280 | "ecore_timer_delay"); | ||
281 | return; | ||
282 | } | ||
283 | |||
284 | _ecore_lock(); | ||
285 | _ecore_timer_delay(timer, add); | ||
286 | _ecore_unlock(); | ||
287 | } | ||
288 | |||
289 | /** | ||
290 | * Get the pending time regarding a timer. | ||
291 | * | ||
292 | * @param timer The timer to learn from. | ||
293 | * @ingroup Ecore_Time_Group | ||
294 | */ | ||
295 | EAPI double | ||
296 | ecore_timer_pending_get(Ecore_Timer *timer) | ||
297 | { | ||
298 | double now; | ||
299 | double ret = 0.0; | ||
300 | |||
301 | _ecore_lock(); | ||
302 | |||
303 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
304 | { | ||
305 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
306 | "ecore_timer_pending_get"); | ||
307 | goto unlock; | ||
308 | } | ||
309 | |||
310 | now = ecore_time_get(); | ||
311 | |||
312 | if (timer->frozen) | ||
313 | ret = timer->pending; | ||
314 | else | ||
315 | ret = timer->at - now; | ||
316 | unlock: | ||
317 | _ecore_unlock(); | ||
318 | return ret; | ||
319 | } | ||
320 | |||
321 | /** | ||
322 | * Pauses a running timer. | ||
323 | * | ||
324 | * @param timer The timer to be paused. | ||
325 | * | ||
326 | * The timer callback won't be called while the timer is paused. The remaining | ||
327 | * time until the timer expires will be saved, so the timer can be resumed with | ||
328 | * that same remaining time to expire, instead of expiring instantly. Use | ||
329 | * ecore_timer_thaw() to resume it. | ||
330 | * | ||
331 | * @note Nothing happens if the timer was already paused. | ||
332 | * | ||
333 | * @see ecore_timer_thaw() | ||
334 | */ | ||
335 | EAPI void | ||
336 | ecore_timer_freeze(Ecore_Timer *timer) | ||
337 | { | ||
338 | double now; | ||
339 | |||
340 | _ecore_lock(); | ||
341 | |||
342 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
343 | { | ||
344 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
345 | "ecore_timer_freeze"); | ||
346 | goto unlock; | ||
347 | } | ||
348 | |||
349 | /* Timer already frozen */ | ||
350 | if (timer->frozen) | ||
351 | goto unlock; | ||
352 | |||
353 | timers = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); | ||
354 | suspended = (Ecore_Timer *)eina_inlist_prepend(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); | ||
355 | |||
356 | now = ecore_time_get(); | ||
357 | |||
358 | timer->pending = timer->at - now; | ||
359 | timer->at = 0.0; | ||
360 | timer->frozen = 1; | ||
361 | unlock: | ||
362 | _ecore_unlock(); | ||
363 | } | ||
364 | |||
365 | /** | ||
366 | * Resumes a frozen (paused) timer. | ||
367 | * | ||
368 | * @param timer The timer to be resumed. | ||
369 | * | ||
370 | * The timer will be resumed from its previous relative position in time. That | ||
371 | * means, if it had X seconds remaining until expire when it was paused, it will | ||
372 | * be started now with those same X seconds remaining to expire again. But | ||
373 | * notice that the interval time won't be touched by this call or by | ||
374 | * ecore_timer_freeze(). | ||
375 | * | ||
376 | * @see ecore_timer_freeze() | ||
377 | */ | ||
378 | EAPI void | ||
379 | ecore_timer_thaw(Ecore_Timer *timer) | ||
380 | { | ||
381 | double now; | ||
382 | |||
383 | _ecore_lock(); | ||
384 | |||
385 | if (!ECORE_MAGIC_CHECK(timer, ECORE_MAGIC_TIMER)) | ||
386 | { | ||
387 | ECORE_MAGIC_FAIL(timer, ECORE_MAGIC_TIMER, | ||
388 | "ecore_timer_thaw"); | ||
389 | goto unlock; | ||
390 | } | ||
391 | |||
392 | /* Timer not frozen */ | ||
393 | if (!timer->frozen) | ||
394 | goto unlock; | ||
395 | |||
396 | suspended = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); | ||
397 | now = ecore_time_get(); | ||
398 | |||
399 | _ecore_timer_set(timer, timer->pending + now, timer->in, timer->func, timer->data); | ||
400 | unlock: | ||
401 | _ecore_unlock(); | ||
402 | } | ||
403 | |||
404 | EAPI char * | ||
405 | ecore_timer_dump(void) | ||
406 | { | ||
407 | #ifdef WANT_ECORE_TIMER_DUMP | ||
408 | Eina_Strbuf *result; | ||
409 | char *out; | ||
410 | Ecore_Timer *tm; | ||
411 | Eina_List *tmp = NULL; | ||
412 | int living_timer = 0; | ||
413 | int unknow_timer = 0; | ||
414 | |||
415 | _ecore_lock(); | ||
416 | result = eina_strbuf_new(); | ||
417 | |||
418 | EINA_INLIST_FOREACH(timers, tm) | ||
419 | tmp = eina_list_sorted_insert(tmp, _ecore_timer_cmp, tm); | ||
420 | |||
421 | EINA_LIST_FREE(tmp, tm) | ||
422 | { | ||
423 | char **strings; | ||
424 | int j; | ||
425 | |||
426 | if (!tm->frozen && !tm->delete_me) | ||
427 | living_timer++; | ||
428 | |||
429 | strings = backtrace_symbols((void **)tm->timer_bt, tm->timer_bt_num); | ||
430 | if (tm->timer_bt_num <= 0 || strings == NULL) | ||
431 | { | ||
432 | unknow_timer++; | ||
433 | continue; | ||
434 | } | ||
435 | |||
436 | eina_strbuf_append_printf(result, "*** timer: %f ***\n", tm->in); | ||
437 | if (tm->frozen) | ||
438 | eina_strbuf_append(result, "FROZEN\n"); | ||
439 | if (tm->delete_me) | ||
440 | eina_strbuf_append(result, "DELETED\n"); | ||
441 | for (j = 0; j < tm->timer_bt_num; j++) | ||
442 | eina_strbuf_append_printf(result, "%s\n", strings[j]); | ||
443 | |||
444 | free(strings); | ||
445 | } | ||
446 | |||
447 | eina_strbuf_append_printf(result, "\n***\nThere is %i living timer.\nWe did lost track of %i timers.\n", living_timer, unknow_timer); | ||
448 | |||
449 | out = eina_strbuf_string_steal(result); | ||
450 | eina_strbuf_free(result); | ||
451 | _ecore_unlock(); | ||
452 | |||
453 | return out; | ||
454 | #else | ||
455 | return NULL; | ||
456 | #endif | ||
457 | } | ||
458 | |||
459 | /** | ||
460 | * @} | ||
461 | */ | ||
462 | |||
463 | Ecore_Timer * | ||
464 | _ecore_timer_loop_add(double in, | ||
465 | Ecore_Task_Cb func, | ||
466 | const void *data) | ||
467 | { | ||
468 | double now; | ||
469 | Ecore_Timer *timer = NULL; | ||
470 | |||
471 | if (!func) return timer; | ||
472 | if (in < 0.0) in = 0.0; | ||
473 | timer = calloc(1, sizeof(Ecore_Timer)); | ||
474 | if (!timer) return timer; | ||
475 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_TIMER); | ||
476 | now = ecore_loop_time_get(); | ||
477 | |||
478 | #ifdef WANT_ECORE_TIMER_DUMP | ||
479 | timer->timer_bt_num = backtrace((void **)(timer->timer_bt), | ||
480 | ECORE_TIMER_DEBUG_BT_NUM); | ||
481 | #endif | ||
482 | _ecore_timer_set(timer, now + in, in, func, (void *)data); | ||
483 | return timer; | ||
484 | } | ||
485 | |||
486 | EAPI void | ||
487 | _ecore_timer_delay(Ecore_Timer *timer, | ||
488 | double add) | ||
489 | { | ||
490 | if (timer->frozen) | ||
491 | { | ||
492 | timer->pending += add; | ||
493 | } | ||
494 | else | ||
495 | { | ||
496 | timers = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); | ||
497 | _ecore_timer_set(timer, timer->at + add, timer->in, timer->func, timer->data); | ||
498 | } | ||
499 | } | ||
500 | |||
501 | void * | ||
502 | _ecore_timer_del(Ecore_Timer *timer) | ||
503 | { | ||
504 | if (timer->frozen && !timer->references) | ||
505 | { | ||
506 | void *data = timer->data; | ||
507 | |||
508 | suspended = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); | ||
509 | |||
510 | if (timer->delete_me) | ||
511 | timers_delete_me--; | ||
512 | |||
513 | free(timer); | ||
514 | return data; | ||
515 | } | ||
516 | |||
517 | EINA_SAFETY_ON_TRUE_RETURN_VAL(timer->delete_me, NULL); | ||
518 | timer->delete_me = 1; | ||
519 | timers_delete_me++; | ||
520 | return timer->data; | ||
521 | } | ||
522 | |||
523 | void | ||
524 | _ecore_timer_shutdown(void) | ||
525 | { | ||
526 | Ecore_Timer *timer; | ||
527 | |||
528 | while ((timer = timers)) | ||
529 | { | ||
530 | timers = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timers)); | ||
531 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); | ||
532 | free(timer); | ||
533 | } | ||
534 | |||
535 | while ((timer = suspended)) | ||
536 | { | ||
537 | suspended = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(suspended)); | ||
538 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); | ||
539 | free(timer); | ||
540 | } | ||
541 | |||
542 | timer_current = NULL; | ||
543 | } | ||
544 | |||
545 | void | ||
546 | _ecore_timer_cleanup(void) | ||
547 | { | ||
548 | Ecore_Timer *l; | ||
549 | int in_use = 0, todo = timers_delete_me, done = 0; | ||
550 | |||
551 | if (!timers_delete_me) return; | ||
552 | for (l = timers; l; ) | ||
553 | { | ||
554 | Ecore_Timer *timer = l; | ||
555 | |||
556 | l = (Ecore_Timer *)EINA_INLIST_GET(l)->next; | ||
557 | if (timer->delete_me) | ||
558 | { | ||
559 | if (timer->references) | ||
560 | { | ||
561 | in_use++; | ||
562 | continue; | ||
563 | } | ||
564 | timers = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); | ||
565 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); | ||
566 | free(timer); | ||
567 | timers_delete_me--; | ||
568 | done++; | ||
569 | if (timers_delete_me == 0) return; | ||
570 | } | ||
571 | } | ||
572 | for (l = suspended; l; ) | ||
573 | { | ||
574 | Ecore_Timer *timer = l; | ||
575 | |||
576 | l = (Ecore_Timer *)EINA_INLIST_GET(l)->next; | ||
577 | if (timer->delete_me) | ||
578 | { | ||
579 | if (timer->references) | ||
580 | { | ||
581 | in_use++; | ||
582 | continue; | ||
583 | } | ||
584 | suspended = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(suspended), EINA_INLIST_GET(timer)); | ||
585 | ECORE_MAGIC_SET(timer, ECORE_MAGIC_NONE); | ||
586 | free(timer); | ||
587 | timers_delete_me--; | ||
588 | done++; | ||
589 | if (timers_delete_me == 0) return; | ||
590 | } | ||
591 | } | ||
592 | |||
593 | if ((!in_use) && (timers_delete_me)) | ||
594 | { | ||
595 | ERR("%d timers to delete, but they were not found!" | ||
596 | "Stats: todo=%d, done=%d, pending=%d, in_use=%d. " | ||
597 | "reset counter.", | ||
598 | timers_delete_me, todo, done, todo - done, in_use); | ||
599 | timers_delete_me = 0; | ||
600 | } | ||
601 | } | ||
602 | |||
603 | void | ||
604 | _ecore_timer_enable_new(void) | ||
605 | { | ||
606 | Ecore_Timer *timer; | ||
607 | |||
608 | if (!timers_added) return; | ||
609 | timers_added = 0; | ||
610 | EINA_INLIST_FOREACH(timers, timer) timer->just_added = 0; | ||
611 | } | ||
612 | |||
613 | int | ||
614 | _ecore_timers_exists(void) | ||
615 | { | ||
616 | Ecore_Timer *timer = timers; | ||
617 | |||
618 | while ((timer) && (timer->delete_me)) | ||
619 | timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next; | ||
620 | |||
621 | return !!timer; | ||
622 | } | ||
623 | |||
624 | static inline Ecore_Timer * | ||
625 | _ecore_timer_first_get(void) | ||
626 | { | ||
627 | Ecore_Timer *timer = timers; | ||
628 | |||
629 | while ((timer) && ((timer->delete_me) || (timer->just_added))) | ||
630 | timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next; | ||
631 | |||
632 | return timer; | ||
633 | } | ||
634 | |||
635 | static inline Ecore_Timer * | ||
636 | _ecore_timer_after_get(Ecore_Timer *base) | ||
637 | { | ||
638 | Ecore_Timer *timer = (Ecore_Timer *)EINA_INLIST_GET(base)->next; | ||
639 | Ecore_Timer *valid_timer = NULL; | ||
640 | double maxtime = base->at + precision; | ||
641 | |||
642 | while ((timer) && (timer->at < maxtime)) | ||
643 | { | ||
644 | if (!((timer->delete_me) || (timer->just_added))) | ||
645 | valid_timer = timer; | ||
646 | timer = (Ecore_Timer *)EINA_INLIST_GET(timer)->next; | ||
647 | } | ||
648 | |||
649 | return valid_timer; | ||
650 | } | ||
651 | |||
652 | double | ||
653 | _ecore_timer_next_get(void) | ||
654 | { | ||
655 | double now; | ||
656 | double in; | ||
657 | Ecore_Timer *first, *second; | ||
658 | |||
659 | first = _ecore_timer_first_get(); | ||
660 | if (!first) return -1; | ||
661 | |||
662 | second = _ecore_timer_after_get(first); | ||
663 | if (second) first = second; | ||
664 | |||
665 | now = ecore_loop_time_get(); | ||
666 | in = first->at - now; | ||
667 | if (in < 0) in = 0; | ||
668 | return in; | ||
669 | } | ||
670 | |||
671 | static inline void | ||
672 | _ecore_timer_reschedule(Ecore_Timer *timer, | ||
673 | double when) | ||
674 | { | ||
675 | if ((timer->delete_me) || (timer->frozen)) return; | ||
676 | |||
677 | timers = (Ecore_Timer *)eina_inlist_remove(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); | ||
678 | |||
679 | /* if the timer would have gone off more than 15 seconds ago, | ||
680 | * assume that the system hung and set the timer to go off | ||
681 | * timer->in from now. this handles system hangs, suspends | ||
682 | * and more, so ecore will only "replay" the timers while | ||
683 | * the system is suspended if it is suspended for less than | ||
684 | * 15 seconds (basically). this also handles if the process | ||
685 | * is stopped in a debugger or IO and other handling gets | ||
686 | * really slow within the main loop. | ||
687 | */ | ||
688 | if ((timer->at + timer->in) < (when - 15.0)) | ||
689 | _ecore_timer_set(timer, when + timer->in, timer->in, timer->func, timer->data); | ||
690 | else | ||
691 | _ecore_timer_set(timer, timer->at + timer->in, timer->in, timer->func, timer->data); | ||
692 | } | ||
693 | |||
694 | /* assume that we hold the ecore lock when entering this function */ | ||
695 | void | ||
696 | _ecore_timer_expired_timers_call(double when) | ||
697 | { | ||
698 | /* call the first expired timer until no expired timers exist */ | ||
699 | while (_ecore_timer_expired_call(when)) ; | ||
700 | } | ||
701 | |||
702 | /* assume that we hold the ecore lock when entering this function */ | ||
703 | int | ||
704 | _ecore_timer_expired_call(double when) | ||
705 | { | ||
706 | if (!timers) return 0; | ||
707 | if (last_check > when) | ||
708 | { | ||
709 | Ecore_Timer *timer; | ||
710 | /* User set time backwards */ | ||
711 | EINA_INLIST_FOREACH(timers, timer) timer->at -= (last_check - when); | ||
712 | } | ||
713 | last_check = when; | ||
714 | |||
715 | if (!timer_current) | ||
716 | { | ||
717 | /* regular main loop, start from head */ | ||
718 | timer_current = timers; | ||
719 | } | ||
720 | else | ||
721 | { | ||
722 | /* recursive main loop, continue from where we were */ | ||
723 | Ecore_Timer *timer_old = timer_current; | ||
724 | timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next; | ||
725 | _ecore_timer_reschedule(timer_old, when); | ||
726 | } | ||
727 | |||
728 | while (timer_current) | ||
729 | { | ||
730 | Ecore_Timer *timer = timer_current; | ||
731 | |||
732 | if (timer->at > when) | ||
733 | { | ||
734 | timer_current = NULL; /* ended walk, next should restart. */ | ||
735 | return 0; | ||
736 | } | ||
737 | |||
738 | if ((timer->just_added) || (timer->delete_me)) | ||
739 | { | ||
740 | timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next; | ||
741 | continue; | ||
742 | } | ||
743 | |||
744 | timer->references++; | ||
745 | if (!_ecore_call_task_cb(timer->func, timer->data)) | ||
746 | { | ||
747 | if (!timer->delete_me) _ecore_timer_del(timer); | ||
748 | } | ||
749 | timer->references--; | ||
750 | |||
751 | if (timer_current) /* may have changed in recursive main loops */ | ||
752 | timer_current = (Ecore_Timer *)EINA_INLIST_GET(timer_current)->next; | ||
753 | |||
754 | _ecore_timer_reschedule(timer, when); | ||
755 | } | ||
756 | return 0; | ||
757 | } | ||
758 | |||
759 | static void | ||
760 | _ecore_timer_set(Ecore_Timer *timer, | ||
761 | double at, | ||
762 | double in, | ||
763 | Ecore_Task_Cb func, | ||
764 | void *data) | ||
765 | { | ||
766 | Ecore_Timer *t2; | ||
767 | |||
768 | timers_added = 1; | ||
769 | timer->at = at; | ||
770 | timer->in = in; | ||
771 | timer->func = func; | ||
772 | timer->data = data; | ||
773 | timer->just_added = 1; | ||
774 | timer->frozen = 0; | ||
775 | timer->pending = 0.0; | ||
776 | if (timers) | ||
777 | { | ||
778 | EINA_INLIST_REVERSE_FOREACH(EINA_INLIST_GET(timers), t2) | ||
779 | { | ||
780 | if (timer->at > t2->at) | ||
781 | { | ||
782 | timers = (Ecore_Timer *)eina_inlist_append_relative(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer), EINA_INLIST_GET(t2)); | ||
783 | return; | ||
784 | } | ||
785 | } | ||
786 | } | ||
787 | timers = (Ecore_Timer *)eina_inlist_prepend(EINA_INLIST_GET(timers), EINA_INLIST_GET(timer)); | ||
788 | } | ||
789 | |||
790 | #ifdef WANT_ECORE_TIMER_DUMP | ||
791 | static int | ||
792 | _ecore_timer_cmp(const void *d1, | ||
793 | const void *d2) | ||
794 | { | ||
795 | const Ecore_Timer *t1 = d1; | ||
796 | const Ecore_Timer *t2 = d2; | ||
797 | |||
798 | return (int)((t1->in - t2->in) * 100); | ||
799 | } | ||
800 | #endif | ||