summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLauro Moura <lauromoura@expertisesolutions.com.br>2018-05-29 19:17:42 -0300
committerLauro Moura <lauromoura@expertisesolutions.com.br>2018-12-03 22:43:50 -0300
commit16a05a06a68e0de89f98bb9a7827fec5ec6f6cbe (patch)
tree5999d922704cc9a7199bc18e5c4d9df8f94d82cf
parentc45f1592e18ed2129027442e9865a12c5b8fb9f6 (diff)
downloadefl-16a05a06a68e0de89f98bb9a7827fec5ec6f6cbe.tar.gz
ecore: Move Coroutine code to efl_loop proper.
-rw-r--r--src/lib/ecore/ecore_main.c754
-rw-r--r--src/lib/ecore/efl_loop.c180
-rw-r--r--src/lib/ecore/efl_loop.eo42
-rw-r--r--src/lib/eina/eina_promise.c3
4 files changed, 223 insertions, 756 deletions
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
index 7d9ca21c44..565e7c2d01 100644
--- a/src/lib/ecore/ecore_main.c
+++ b/src/lib/ecore/ecore_main.c
@@ -2837,758 +2837,4 @@ err:
}
#endif
-Eo *_mainloop_singleton = NULL;
-EOLIAN static Efl_Loop *
-_efl_loop_main_get(Efl_Class *klass EINA_UNUSED, void *_pd EINA_UNUSED)
-{
- if (!_mainloop_singleton)
- {
- _mainloop_singleton = efl_add(EFL_LOOP_CLASS, NULL);
- }
-
- return _mainloop_singleton;
-}
-
-EAPI Eo *
-ecore_main_loop_get(void)
-{
- return efl_loop_main_get(EFL_LOOP_CLASS);
-}
-
-EOLIAN static void
-_efl_loop_iterate(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
-{
- ecore_main_loop_iterate();
-}
-
-EOLIAN static int _efl_loop_iterate_may_block(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, int may_block)
-{
- return ecore_main_loop_iterate_may_block(may_block);
-}
-
-EOLIAN static unsigned char
-_efl_loop_begin(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
-{
- ecore_main_loop_begin();
- return _ecore_exit_code;
-}
-
-EOLIAN static void
-_efl_loop_quit(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED, unsigned char exit_code)
-{
- ecore_main_loop_quit();
- _ecore_exit_code = exit_code;
-}
-
-EOLIAN static Efl_Object *
-_efl_loop_efl_object_provider_find(Eo *obj, Efl_Loop_Data *pd, const Efl_Object *klass)
-{
- Efl_Object *r;
-
- if (klass == EFL_LOOP_CLASS) return obj;
-
- r = eina_hash_find(pd->providers, &klass);
- if (r) return r;
-
- return efl_provider_find(efl_super(obj, EFL_LOOP_CLASS), klass);
-}
-
-static void
-_poll_trigger(void *data, const Efl_Event *event)
-{
- Eo *parent = efl_parent_get(event->object);
-
- efl_event_callback_call(parent, data, NULL);
-}
-
-static void
-_check_event_catcher_add(void *data, const Efl_Event *event)
-{
- const Efl_Callback_Array_Item *array = event->info;
- Efl_Loop_Data *pd = data;
- int i;
-
- for (i = 0; array[i].desc != NULL; i++)
- {
- if (array[i].desc == EFL_LOOP_EVENT_IDLE)
- {
- ++pd->idlers;
- }
- // XXX: all the below are kind of bad. ecore_pollers were special.
- // they all woke up at the SAME time based on interval, (all pollers
- // of interval 1 woke up together, those with 2 woke up when 1 and
- // 2 woke up, 4 woke up together along with 1 and 2 etc.
- // the below means they will just go off whenever but at a pre
- // defined interval - 1/60th, 6 and 66 seconds. not really great
- // pollers probably should be less frequent that 1/60th even on poll
- // high, medium probably down to 1-2 sec and low - yes maybe 30 or 60
- // sec... still - not timed to wake up together. :(
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
- {
- if (!pd->poll_high)
- {
- // Would be better to have it in sync with normal wake up
- // of the main loop for better energy efficiency, I guess.
- pd->poll_high = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
- efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_HIGH),
- efl_loop_timer_interval_set(efl_added, 1.0/60.0));
- }
- ++pd->pollers.high;
- }
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
- {
- if (!pd->poll_medium)
- {
- pd->poll_medium = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
- efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_MEDIUM),
- efl_loop_timer_interval_set(efl_added, 6));
- }
- ++pd->pollers.medium;
- }
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
- {
- if (!pd->poll_low)
- {
- pd->poll_low = efl_add(EFL_LOOP_TIMER_CLASS, event->object,
- efl_event_callback_add(efl_added, EFL_LOOP_TIMER_EVENT_TICK, _poll_trigger, EFL_LOOP_EVENT_POLL_LOW),
- efl_loop_timer_interval_set(efl_added, 66));
- }
- ++pd->pollers.low;
- }
- }
-}
-
-static void
-_check_event_catcher_del(void *data, const Efl_Event *event)
-{
- const Efl_Callback_Array_Item *array = event->info;
- Efl_Loop_Data *pd = data;
- int i;
-
- for (i = 0; array[i].desc != NULL; i++)
- {
- if (array[i].desc == EFL_LOOP_EVENT_IDLE)
- {
- --pd->idlers;
- }
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_HIGH)
- {
- --pd->pollers.high;
- if (!pd->pollers.high)
- {
- ecore_timer_del(pd->poll_high);
- pd->poll_high = NULL;
- }
- }
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_MEDIUM)
- {
- --pd->pollers.medium;
- if (!pd->pollers.medium)
- {
- ecore_timer_del(pd->poll_medium);
- pd->poll_medium = NULL;
- }
- }
- else if (array[i].desc == EFL_LOOP_EVENT_POLL_LOW)
- {
- --pd->pollers.low;
- if (!pd->pollers.low)
- {
- ecore_timer_del(pd->poll_low);
- pd->poll_low = NULL;
- }
- }
- }
-}
-
-EFL_CALLBACKS_ARRAY_DEFINE(event_catcher_watch,
- { EFL_EVENT_CALLBACK_ADD, _check_event_catcher_add },
- { EFL_EVENT_CALLBACK_DEL, _check_event_catcher_del });
-
-EOLIAN static Efl_Object *
-_efl_loop_efl_object_constructor(Eo *obj, Efl_Loop_Data *pd)
-{
- obj = efl_constructor(efl_super(obj, EFL_LOOP_CLASS));
- if (!obj) return NULL;
-
- efl_event_callback_array_add(obj, event_catcher_watch(), pd);
-
- pd->providers = eina_hash_pointer_new((void*) efl_unref);
-
- return obj;
-}
-
-EOLIAN static void
-_efl_loop_efl_object_destructor(Eo *obj, Efl_Loop_Data *pd)
-{
- eina_hash_free(pd->providers);
-
- efl_del(pd->poll_low);
- efl_del(pd->poll_medium);
- efl_del(pd->poll_high);
-
- efl_destructor(efl_super(obj, EFL_LOOP_CLASS));
-}
-
-typedef struct _Efl_Internal_Promise Efl_Internal_Promise;
-struct _Efl_Internal_Promise
-{
- union {
- Ecore_Job *job;
- Efl_Loop_Timer *timer;
- } u;
- Efl_Promise *promise;
-
- const void *data;
-
- Eina_Bool job_is : 1;
-};
-
-static void
-_efl_loop_job_cb(void *data)
-{
- Efl_Internal_Promise *j = data;
-
- efl_promise_value_set(j->promise, (void*) j->data, NULL);
-
- free(j);
-}
-
-static void
-_efl_loop_arguments_cleanup(Eina_Array *arga)
-{
- Eina_Stringshare *s;
-
- while ((s = eina_array_pop(arga)))
- eina_stringshare_del(s);
- eina_array_free(arga);
-}
-
-static void
-_efl_loop_arguments_send(void *data, const Efl_Event *ev EINA_UNUSED)
-{
- static Eina_Bool initialization = EINA_TRUE;
- Efl_Loop_Arguments arge;
- Eina_Array *arga = data;
-
- arge.argv = arga;
- arge.initialization = initialization;
- initialization = EINA_FALSE;
-
- efl_event_callback_call(ecore_main_loop_get(), EFL_LOOP_EVENT_ARGUMENTS, &arge);
-
- _efl_loop_arguments_cleanup(arga);
-}
-
-static void
-_efl_loop_arguments_cancel(void *data, const Efl_Event *ev EINA_UNUSED)
-{
- _efl_loop_arguments_cleanup(data);
-}
-
-// It doesn't make sense to send those argument to any other mainloop
-// As it also doesn't make sense to allow anyone to override this, so
-// should be internal for sure, not even protected.
-EAPI void
-ecore_loop_arguments_send(int argc, const char **argv)
-{
- Efl_Future *job;
- Eina_Array *arga;
- int i = 0;
-
- arga = eina_array_new(argc);
- for (i = 0; i < argc; i++)
- eina_array_push(arga, eina_stringshare_add(argv[i]));
-
- job = efl_loop_job(ecore_main_loop_get(), NULL);
- efl_future_then(job, _efl_loop_arguments_send, _efl_loop_arguments_cancel, NULL, arga);
-}
-
-static void _efl_loop_timeout_force_cancel_cb(void *data, const Efl_Event *event EINA_UNUSED);
-static void _efl_loop_timeout_cb(void *data, const Efl_Event *event EINA_UNUSED);
-
-// Only one main loop handle for now
-void
-ecore_loop_future_register(Efl_Loop *l EINA_UNUSED, Efl_Future *f)
-{
- _pending_futures = eina_list_append(_pending_futures, f);
-}
-
-void
-ecore_loop_future_unregister(Efl_Loop *l EINA_UNUSED, Efl_Future *f)
-{
- _pending_futures = eina_list_remove(_pending_futures, f);
-}
-
-void
-ecore_loop_promise_register(Efl_Loop *l EINA_UNUSED, Efl_Promise *p)
-{
- _pending_promises = eina_list_append(_pending_promises, p);
-}
-
-void
-ecore_loop_promise_unregister(Efl_Loop *l EINA_UNUSED, Efl_Promise *p)
-{
- _pending_promises = eina_list_remove(_pending_promises, p);
-}
-
-EFL_CALLBACKS_ARRAY_DEFINE(timeout,
- { EFL_LOOP_TIMER_EVENT_TICK, _efl_loop_timeout_cb },
- { EFL_EVENT_DEL, _efl_loop_timeout_force_cancel_cb });
-
-static Eina_Future *
-_efl_loop_Eina_FutureXXX_job(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
-{
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj,
- eina_future_resolved(efl_loop_future_scheduler_get(obj),
- EINA_VALUE_EMPTY));
-}
-
-static void
-_efl_loop_Eina_FutureXXX_idle_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
-{
- Efl_Loop_Promise_Simple_Data *d = data;
- ecore_idler_del(d->idler);
- efl_loop_promise_simple_data_mp_free(d);
-}
-
-static Eina_Bool
-_efl_loop_Eina_FutureXXX_idle_done(void *data)
-{
- Efl_Loop_Promise_Simple_Data *d = data;
- eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
- efl_loop_promise_simple_data_mp_free(d);
- return EINA_FALSE;
-}
-
-static Eina_Future *
-_efl_loop_Eina_FutureXXX_idle(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED)
-{
- Efl_Loop_Promise_Simple_Data *d;
- Eina_Promise *p;
-
- d = efl_loop_promise_simple_data_calloc(1);
- EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
-
- d->idler = ecore_idler_add(_efl_loop_Eina_FutureXXX_idle_done, d);
- EINA_SAFETY_ON_NULL_GOTO(d->idler, idler_error);
-
- p = eina_promise_new(efl_loop_future_scheduler_get(obj),
- _efl_loop_Eina_FutureXXX_idle_cancel, d);
- // d is dead if p is NULL
- EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
- d->promise = p;
-
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj, eina_future_new(p));
-
- idler_error:
- efl_loop_promise_simple_data_mp_free(d);
- return NULL;
-}
-
-static void
-_efl_loop_Eina_FutureXXX_timeout_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
-{
- Efl_Loop_Promise_Simple_Data *d = data;
- ecore_timer_del(d->timer);
- efl_loop_promise_simple_data_mp_free(d);
-}
-
-static Eina_Bool
-_efl_loop_Eina_FutureXXX_timeout_done(void *data)
-{
- Efl_Loop_Promise_Simple_Data *d = data;
- eina_promise_resolve(d->promise, EINA_VALUE_EMPTY);
- efl_loop_promise_simple_data_mp_free(d);
- return EINA_FALSE;
-}
-
-static Eina_Future *
-_efl_loop_Eina_FutureXXX_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double time)
-{
- Efl_Loop_Promise_Simple_Data *d;
- Eina_Promise *p;
-
- d = efl_loop_promise_simple_data_calloc(1);
- EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
-
- d->timer = ecore_timer_add(time, _efl_loop_Eina_FutureXXX_timeout_done, d);
- EINA_SAFETY_ON_NULL_GOTO(d->timer, timer_error);
-
- p = eina_promise_new(efl_loop_future_scheduler_get(obj),
- _efl_loop_Eina_FutureXXX_timeout_cancel, d);
- // d is dead if p is NULL
- EINA_SAFETY_ON_NULL_RETURN_VAL(p, NULL);
- d->promise = p;
-
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj, eina_future_new(p));
-
- timer_error:
- efl_loop_promise_simple_data_mp_free(d);
- return NULL;
-}
-
-typedef struct _Efl_Loop_Coro {
- Eina_Promise *promise;
- Eina_Coro *coro;
- Efl_Loop *loop;
- Eina_Future *scheduled;
- Efl_Loop_Coro_Cb func;
- const void *func_data;
- Eina_Free_Cb func_free_cb;
- Efl_Loop_Coro_Prio prio;
- Eina_Value value;
-} Efl_Loop_Coro;
-
-static void
-_efl_loop_coro_free(Efl_Loop_Coro *lc)
-{
- if (lc->func_free_cb) lc->func_free_cb((void *)lc->func_data);
- if (lc->scheduled) eina_future_cancel(lc->scheduled);
- eina_value_flush(&lc->value);
- efl_unref(lc->loop);
- free(lc);
-}
-
-static void _efl_loop_coro_reschedule(Efl_Loop_Coro *lc);
-
-static Eina_Value
-_efl_loop_coro_schedule_resolved(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED)
-{
- Efl_Loop_Coro *lc = data;
- Eina_Future *awaiting = NULL;
-
- if (value.type == EINA_VALUE_TYPE_ERROR)
- {
- Eina_Error err;
- eina_value_get(&value, &err);
- ERR("coro %p scheduled got error %s, try again.",
- lc, eina_error_msg_get(err));
- }
- else if (!eina_coro_run(&lc->coro, NULL, &awaiting))
- {
- INF("coroutine %p finished with value type=%p (%s)",
- lc, lc->value.type,
- lc->value.type ? lc->value.type->name : "EMPTY");
-
- eina_promise_resolve(lc->promise, lc->value);
- lc->value = EINA_VALUE_EMPTY; // owned by promise
- _efl_loop_coro_free(lc);
- return value;
- }
- else if (awaiting)
- {
- DBG("coroutine %p is awaiting for future %p, do not reschedule", lc, awaiting);
- eina_future_chain(awaiting,
- {
- .cb = _efl_loop_coro_schedule_resolved,
- .data = lc,
- .storage = &lc->scheduled,
- },
- efl_future_cb(lc->loop));
- }
- else _efl_loop_coro_reschedule(lc);
-
- return value;
-}
-
-static void
-_efl_loop_coro_reschedule(Efl_Loop_Coro *lc)
-{
- Eina_Future *f;
-
- // high uses 0-timeout instead of job, since job
- // is implemented using events and the Ecore implementation
- // will never run timers or anything else, just the new jobs :-/
- //
- // TODO: bug report ecore_main loop bug.
- if (lc->prio == EFL_LOOP_CORO_PRIO_HIGH)
- f = efl_loop_Eina_FutureXXX_timeout(lc->loop, 0);
- else
- f = efl_loop_Eina_FutureXXX_idle(lc->loop);
-
- DBG("coroutine %p rescheduled as future=%p", lc, f);
-
- // NOTE: efl_future_cb() doesn't allow for extra 'data', so it matches
- // methods more easily. However we need 'lc' and we can't store in
- // loop since we'd not know the key for efl_key_data_get().
- // Easy solution: use 2 futures, one to bind and another to resolve.
- eina_future_chain(f,
- {
- .cb = _efl_loop_coro_schedule_resolved,
- .data = lc,
- .storage = &lc->scheduled,
- },
- efl_future_cb(lc->loop));
-}
-
-static void
-_efl_loop_coro_cancel(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
-{
- Efl_Loop_Coro *lc = data;
-
- INF("canceled coroutine %p (coro=%p)", lc, lc->coro);
-
- eina_coro_cancel(&lc->coro);
-
- _efl_loop_coro_free(lc);
-}
-
-static const void *
-_efl_loop_coro_cb(void *data, Eina_Bool canceled, Eina_Coro *coro)
-{
- Efl_Loop_Coro *lc = data;
-
- if (canceled) lc->value = eina_value_error_init(ECANCELED);
- else lc->value = lc->func((void *)lc->func_data, coro, lc->loop);
-
- return lc;
-}
-
-static Eina_Future *
-_efl_loop_coro(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, Efl_Loop_Coro_Prio prio, void *func_data, Efl_Loop_Coro_Cb func, Eina_Free_Cb func_free_cb)
-{
- Efl_Loop_Coro *lc;
- Eina_Promise *p;
- Eina_Future *f;
-
- EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
-
- lc = calloc(1, sizeof(Efl_Loop_Coro));
- EINA_SAFETY_ON_NULL_GOTO(lc, calloc_failed);
-
- lc->loop = efl_ref(obj);
- lc->func = func;
- lc->func_data = func_data;
- lc->func_free_cb = func_free_cb;
- lc->prio = prio;
-
- lc->coro = eina_coro_new(_efl_loop_coro_cb, lc, EINA_CORO_STACK_SIZE_DEFAULT);
- EINA_SAFETY_ON_NULL_GOTO(lc, coro_failed);
-
- p = eina_promise_new(efl_loop_future_scheduler_get(obj),
- _efl_loop_coro_cancel, lc);
- // lc is dead if p is NULL
- EINA_SAFETY_ON_NULL_GOTO(p, promise_failed);
- lc->promise = p;
-
- // must be done prior to reschedule, as it may resolve on errors
- // and promises without futures are simply ignored, will remain
- // alive.
- f = eina_future_new(p);
-
- _efl_loop_coro_reschedule(lc);
-
- INF("new coroutine %p (coro=%p)", lc, lc->coro);
-
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj, f);
-
- promise_failed:
- // _efl_loop_coro_cancel() was called, func was run... just return.
-
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj,
- eina_future_resolved(efl_loop_future_scheduler_get(obj),
- eina_value_error_init(ENOMEM)));
-
- coro_failed:
- _efl_loop_coro_free(lc);
-
- calloc_failed:
- if (func_free_cb) func_free_cb((void *)func_data);
-
- // NOTE: Eolian should do efl_future_then() to bind future to object.
- return efl_future_Eina_FutureXXX_then(obj,
- eina_future_resolved(efl_loop_future_scheduler_get(obj),
- eina_value_error_init(ENOMEM)));
-}
-
-
-/* This event will be triggered when the main loop is destroyed and destroy its timers along */
-static void _efl_loop_internal_cancel(Efl_Internal_Promise *p);
-
-static void
-_efl_loop_timeout_force_cancel_cb(void *data, const Efl_Event *event EINA_UNUSED)
-{
- _efl_loop_internal_cancel(data);
-}
-
-static void _efl_loop_job_cancel(void* data, const Efl_Event *ev EINA_UNUSED);
-
-static void
-_efl_loop_timeout_cb(void *data, const Efl_Event *event EINA_UNUSED)
-{
- Efl_Internal_Promise *t = data;
-
- efl_promise_value_set(t->promise, (void*) t->data, NULL);
- efl_del(t->promise);
-
- efl_event_callback_array_del(t->u.timer, timeout(), t);
- efl_del(t->u.timer);
-}
-
-static void
-_efl_loop_internal_cancel(Efl_Internal_Promise *p)
-{
- efl_promise_failed_set(p->promise, EINA_ERROR_FUTURE_CANCEL);
- efl_del(p->promise);
- free(p);
-}
-
-static void
-_efl_loop_job_cancel(void* data, const Efl_Event *ev EINA_UNUSED)
-{
- Efl_Internal_Promise *j = data;
-
- if (j->job_is)
- {
- ecore_job_del(j->u.job);
- }
- else
- {
- efl_event_callback_array_del(j->u.timer, timeout(), j);
- efl_del(j->u.timer);
- }
-
- _efl_loop_internal_cancel(j);
-}
-
-static Efl_Internal_Promise *
-_efl_internal_promise_new(Efl_Promise* promise, const void *data)
-{
- Efl_Internal_Promise *p;
-
- p = calloc(1, sizeof (Efl_Internal_Promise));
- if (!p) return NULL;
-
- efl_event_callback_add(promise, EFL_PROMISE_EVENT_FUTURE_NONE, _efl_loop_job_cancel, p);
-
- p->promise = promise;
- p->data = data;
-
- return p;
-}
-
-static Efl_Future *
-_efl_loop_job(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, const void *data)
-{
- Efl_Internal_Promise *j;
- Efl_Object *promise;
-
- promise = efl_add(EFL_PROMISE_CLASS, obj);
- if (!promise) return NULL;
-
- j = _efl_internal_promise_new(promise, data);
- if (!j) goto on_error;
-
- j->job_is = EINA_TRUE;
- j->u.job = ecore_job_add(_efl_loop_job_cb, j);
- if (!j->u.job) goto on_error;
-
- return efl_promise_future_get(promise);
-
- on_error:
- efl_del(promise);
- free(j);
-
- return NULL;
-}
-
-static Efl_Future *
-_efl_loop_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double time, const void *data)
-{
- Efl_Internal_Promise *t;
- Efl_Object *promise;
-
- promise = efl_add(EFL_PROMISE_CLASS, obj);
- if (!promise) return NULL;
-
- t = _efl_internal_promise_new(promise, data);
- if (!t) goto on_error;
-
- t->job_is = EINA_FALSE;
- t->u.timer = efl_add(EFL_LOOP_TIMER_CLASS, obj,
- efl_loop_timer_interval_set(efl_added, time),
- efl_event_callback_array_add(efl_added, timeout(), t));
-
- if (!t->u.timer) goto on_error;
-
- return efl_promise_future_get(promise);
-
- on_error:
- efl_del(promise);
- free(t);
-
- return NULL;
-}
-
-static Eina_Bool
-_efl_loop_register(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, const Efl_Class *klass, const Efl_Object *provider)
-{
- // The passed object does not provide that said class.
- if (!efl_isa(provider, klass)) return EINA_FALSE;
-
- // Note: I would prefer to use efl_xref here, but I can't figure a nice way to
- // call efl_xunref on hash destruction.
- return eina_hash_add(pd->providers, &klass, efl_ref(provider));
-}
-
-static Eina_Bool
-_efl_loop_unregister(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd, const Efl_Class *klass, const Efl_Object *provider)
-{
- return eina_hash_del(pd->providers, &klass, provider);
-}
-
-Efl_Version _app_efl_version = { 0, 0, 0, 0, NULL, NULL };
-
-EWAPI void
-efl_build_version_set(int vmaj, int vmin, int vmic, int revision,
- const char *flavor, const char *build_id)
-{
- // note: EFL has not been initialized yet at this point (ie. no eina call)
- _app_efl_version.major = vmaj;
- _app_efl_version.minor = vmin;
- _app_efl_version.micro = vmic;
- _app_efl_version.revision = revision;
- free((char *) _app_efl_version.flavor);
- free((char *) _app_efl_version.build_id);
- _app_efl_version.flavor = flavor ? strdup(flavor) : NULL;
- _app_efl_version.build_id = build_id ? strdup(build_id) : NULL;
-}
-
-EOLIAN static const Efl_Version *
-_efl_loop_app_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
-{
- return &_app_efl_version;
-}
-
-EOLIAN static const Efl_Version *
-_efl_loop_efl_version_get(Eo *obj EINA_UNUSED, Efl_Loop_Data *pd EINA_UNUSED)
-{
- /* vanilla EFL: flavor = NULL */
- static const Efl_Version version = {
- .major = VMAJ,
- .minor = VMIN,
- .micro = VMIC,
- .revision = VREV,
- .build_id = EFL_BUILD_ID,
- .flavor = NULL
- };
-
- return &version;
-}
-
-EOLIAN static Eina_Future_Scheduler *
-_efl_loop_future_scheduler_get(Eo *obj EINA_UNUSED,
- Efl_Loop_Data *pd EINA_UNUSED)
-{
- return _ecore_event_future_scheduler_get();
-}
-
-
-#include "efl_loop.eo.c"
diff --git a/src/lib/ecore/efl_loop.c b/src/lib/ecore/efl_loop.c
index 5d24608af3..ebaf157b46 100644
--- a/src/lib/ecore/efl_loop.c
+++ b/src/lib/ecore/efl_loop.c
@@ -914,4 +914,184 @@ efl_loop_promise_new(const Eo *obj, Eina_Promise_Cancel_Cb cancel_cb, const void
cancel_cb, data);
}
+typedef struct _Efl_Loop_Coro {
+ Eina_Promise *promise;
+ Eina_Coro *coro;
+ Efl_Loop *loop;
+ Eina_Future *scheduled;
+ Efl_Loop_Coro_Cb func;
+ const void *func_data;
+ Eina_Free_Cb func_free_cb;
+ Efl_Loop_Coro_Prio prio;
+ Eina_Value value;
+} Efl_Loop_Coro;
+
+static void
+_efl_loop_coro_free(Efl_Loop_Coro *lc)
+{
+ if (lc->func_free_cb) lc->func_free_cb((void *)lc->func_data);
+ if (lc->scheduled) eina_future_cancel(lc->scheduled);
+ eina_value_flush(&lc->value);
+ efl_unref(lc->loop);
+ free(lc);
+}
+
+static void _efl_loop_coro_reschedule(Efl_Loop_Coro *lc);
+
+static Eina_Value
+_efl_loop_coro_schedule_resolved(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Efl_Loop_Coro *lc = data;
+ Eina_Future *awaiting = NULL;
+
+ if (value.type == EINA_VALUE_TYPE_ERROR)
+ {
+ Eina_Error err;
+ eina_value_get(&value, &err);
+ ERR("coro %p scheduled got error %s, try again.",
+ lc, eina_error_msg_get(err));
+ }
+ else if (!eina_coro_run(&lc->coro, NULL, &awaiting))
+ {
+ INF("coroutine %p finished with value type=%p (%s)",
+ lc, lc->value.type,
+ lc->value.type ? lc->value.type->name : "EMPTY");
+
+ eina_promise_resolve(lc->promise, lc->value);
+ lc->value = EINA_VALUE_EMPTY; // owned by promise
+ _efl_loop_coro_free(lc);
+ return value;
+ }
+ else if (awaiting)
+ {
+ DBG("coroutine %p is awaiting for future %p, do not reschedule", lc, awaiting);
+ eina_future_chain(awaiting,
+ {
+ .cb = _efl_loop_coro_schedule_resolved,
+ .data = lc,
+ .storage = &lc->scheduled,
+ },
+ efl_future_cb(lc->loop));
+ }
+ else _efl_loop_coro_reschedule(lc);
+
+ return value;
+}
+
+static void
+_efl_loop_coro_reschedule(Efl_Loop_Coro *lc)
+{
+ Eina_Future *f;
+
+ // high uses 0-timeout instead of job, since job
+ // is implemented using events and the Ecore implementation
+ // will never run timers or anything else, just the new jobs :-/
+ //
+ // TODO: bug report ecore_main loop bug.
+ if (lc->prio == EFL_LOOP_CORO_PRIO_HIGH)
+ f = efl_loop_timeout(lc->loop, 0);
+ else
+ f = efl_loop_idle(lc->loop);
+
+ DBG("coroutine %p rescheduled as future=%p", lc, f);
+
+ // NOTE: efl_future_cb() doesn't allow for extra 'data', so it matches
+ // methods more easily. However we need 'lc' and we can't store in
+ // loop since we'd not know the key for efl_key_data_get().
+ // Easy solution: use 2 futures, one to bind and another to resolve.
+ eina_future_chain(f,
+ {
+ .cb = _efl_loop_coro_schedule_resolved,
+ .data = lc,
+ .storage = &lc->scheduled,
+ },
+ efl_future_cb(lc->loop));
+}
+
+static void
+_efl_loop_coro_cancel(void *data, const Eina_Promise *dead_promise EINA_UNUSED)
+{
+ Efl_Loop_Coro *lc = data;
+
+ INF("canceled coroutine %p (coro=%p)", lc, lc->coro);
+
+ eina_coro_cancel(&lc->coro);
+
+ _efl_loop_coro_free(lc);
+}
+
+static const void *
+_efl_loop_coro_cb(void *data, Eina_Bool canceled, Eina_Coro *coro)
+{
+ Efl_Loop_Coro *lc = data;
+
+ if (canceled) lc->value = eina_value_error_init(ECANCELED);
+ else lc->value = lc->func((void *)lc->func_data, coro, lc->loop);
+
+ return lc;
+}
+
+static Eina_Future *
+_efl_loop_coro(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, Efl_Loop_Coro_Prio prio, void *func_data, Efl_Loop_Coro_Cb func, Eina_Free_Cb func_free_cb)
+{
+ Efl_Loop_Coro *lc;
+ Eina_Promise *p;
+ Eina_Future *f;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
+
+ lc = calloc(1, sizeof(Efl_Loop_Coro));
+ EINA_SAFETY_ON_NULL_GOTO(lc, calloc_failed);
+
+ lc->loop = efl_ref(obj);
+ lc->func = func;
+ lc->func_data = func_data;
+ lc->func_free_cb = func_free_cb;
+ lc->prio = prio;
+
+ lc->coro = eina_coro_new(_efl_loop_coro_cb, lc, EINA_CORO_STACK_SIZE_DEFAULT);
+ EINA_SAFETY_ON_NULL_GOTO(lc, coro_failed);
+
+ p = eina_promise_new(efl_loop_future_scheduler_get(obj),
+ _efl_loop_coro_cancel, lc);
+ // lc is dead if p is NULL
+ EINA_SAFETY_ON_NULL_GOTO(p, promise_failed);
+ lc->promise = p;
+
+ // must be done prior to reschedule, as it may resolve on errors
+ // and promises without futures are simply ignored, will remain
+ // alive.
+ f = eina_future_new(p);
+
+ _efl_loop_coro_reschedule(lc);
+
+ INF("new coroutine %p (coro=%p)", lc, lc->coro);
+
+ // NOTE: Eolian should do efl_future_then() to bind future to object.
+ return efl_future_then(obj, f);
+
+ promise_failed:
+ // _efl_loop_coro_cancel() was called, func was run... just return.
+
+ // NOTE: Eolian should do efl_future_then() to bind future to object.
+ return efl_future_then(obj,
+ eina_future_resolved(efl_loop_future_scheduler_get(obj),
+ eina_value_error_init(ENOMEM)));
+ /* return eina_future_resolved(efl_loop_future_scheduler_get(obj), */
+ /* eina_value_error_init(ENOMEM)); */
+
+ coro_failed:
+ _efl_loop_coro_free(lc);
+
+ calloc_failed:
+ if (func_free_cb) func_free_cb((void *)func_data);
+
+ // NOTE: Eolian should do efl_future_then() to bind future to object.
+ return efl_future_then(obj,
+ eina_future_resolved(efl_loop_future_scheduler_get(obj),
+ eina_value_error_init(ENOMEM)));
+ /* return eina_future_resolved(efl_loop_future_scheduler_get(obj), */
+ /* eina_value_error_init(ENOMEM)); */
+}
+
#include "efl_loop.eo.c"
diff --git a/src/lib/ecore/efl_loop.eo b/src/lib/ecore/efl_loop.eo
index 4a2b31deda..f1022e714a 100644
--- a/src/lib/ecore/efl_loop.eo
+++ b/src/lib/ecore/efl_loop.eo
@@ -3,6 +3,46 @@ struct Efl.Loop_Arguments {
argv: const(array<const(stringshare)>); [[Array with loop arguments]]
initialization: bool; [[Set to $true when the program should initialize its internal state. This happen once per process instance.]]
}
+enum Efl.Loop.Coro.Prio {
+ [[Priority class for the coroutine.]]
+
+ high = 0, [[high priority coroutine, scheduled using zero-timers (will expire as soon as possible).]]
+ idle, [[low priority coroutine, scheduled when nothing else should run]]
+
+}
+
+
+function Efl.Loop.Coro.Cb {
+ [[Coroutine function
+
+ It will be called back from the
+ coroutine environment and when executed it's guaranteed that the
+ main loop will be paused, so shared resources are safe to access
+ (no locks are required).
+
+ \@note Eina_Coro may use threads, then take care to handle
+ thread-local-storage (TLS) details properly, eventually you
+ may consider eina_coro_hook_add() to be informed when the
+ main or coroutine will exit and enter. For instance this is
+ used by Efl_Object (handled transparently for the user).
+ ]]
+
+ params {
+ coro: ptr(Eina.Coro); [[The coroutine handle, used to $eina_coro_yield() and voluntarily give back control to the main loop until it's rescheduled.]]
+ loop: Efl.Loop; [[The loop that schedules this coroutine.]]
+ }
+ return: any_value; [[Value that will resolve the promise,
+ being delivered to the future chain
+ attached to the coroutine. Note that the
+ value will be owned by the Efl_Loop_Coro
+ and Eina_Future subsystems and will be
+ flushed (eina_value_flush()) once
+ unused. Its contents must survive the
+ function return, that is, it shouldn't
+ keep pointers to the stack.
+ ]]
+};
+
class Efl.Loop extends Efl.Task
{
@@ -101,7 +141,7 @@ class Efl.Loop extends Efl.Task
@in priority: Efl.Loop.Coro.Prio; [[The priority used to schedule the coroutine.]]
@in func: Efl.Loop.Coro.Cb @nonull; [[The function to run as a coroutine.]]
}
- return: future<void> @owned; /* future<> */; [[The future handle, it provides the value returned by $func once it exits.]]
+ return: future<void> @owned; [[The future handle, it provides the value returned by $func once it exits.]]
}
timeout {
[[A future promise that will be resolved from a clean main
diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c
index 48f9c2ad7c..dc134574b1 100644
--- a/src/lib/eina/eina_promise.c
+++ b/src/lib/eina/eina_promise.c
@@ -1506,7 +1506,8 @@ eina_future_await(Eina_Future *f, Eina_Coro *coro, const Eina_Value_Type *succes
value.type, value.type ? value.type->name : "EMPTY");
eina_value_flush(&value);
- value = eina_value_error_init(EINVAL);
+ eina_value_setup(&value, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&value, EINVAL);
}
log: