summaryrefslogtreecommitdiff
path: root/src/lib/ecore/ecore_main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/ecore/ecore_main.c')
-rw-r--r--src/lib/ecore/ecore_main.c176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/lib/ecore/ecore_main.c b/src/lib/ecore/ecore_main.c
index 6819a05d7e..2e96f1b6ce 100644
--- a/src/lib/ecore/ecore_main.c
+++ b/src/lib/ecore/ecore_main.c
@@ -3216,6 +3216,182 @@ _efl_loop_Eina_FutureXXX_timeout(Eo *obj, Efl_Loop_Data *pd EINA_UNUSED, double
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);