cedric pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=0789156c86fb51cadbf173bbb230a4d2ddef9612
commit 0789156c86fb51cadbf173bbb230a4d2ddef9612 Author: Cedric Bail <ced...@osg.samsung.com> Date: Wed Jul 27 09:56:07 2016 -0700 ecore: add efl_future_race. --- src/lib/ecore/Ecore_Eo.h | 15 +++ src/lib/ecore/efl_promise.c | 245 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 235 insertions(+), 25 deletions(-) diff --git a/src/lib/ecore/Ecore_Eo.h b/src/lib/ecore/Ecore_Eo.h index 1209232..62c4192 100644 --- a/src/lib/ecore/Ecore_Eo.h +++ b/src/lib/ecore/Ecore_Eo.h @@ -74,6 +74,21 @@ EAPI Efl_Future *efl_future_iterator_all(Eina_Iterator *it); #define efl_future_all(...) efl_future_all_internal(__VA_ARGS__, NULL) +typedef struct _Efl_Future_Race_Success Efl_Future_Race_Success; +typedef struct _Efl_Future_Composite_Progress Efl_Future_Race_Progress; + +struct _Efl_Future_Race_Success +{ + Efl_Future *winner; + void *value; + + unsigned int index; +}; + +EAPI Efl_Future *efl_future_race_internal(Efl_Future *f1, ...); + +#define efl_future_race(...) efl_future_race_internal(__VA_ARGS__, NULL) + /** * @} */ diff --git a/src/lib/ecore/efl_promise.c b/src/lib/ecore/efl_promise.c index 275c56a..72dfdde 100644 --- a/src/lib/ecore/efl_promise.c +++ b/src/lib/ecore/efl_promise.c @@ -588,10 +588,12 @@ _efl_promise_efl_object_destructor(Eo *obj, Efl_Promise_Data *pd) efl_destructor(efl_super(obj, EFL_PROMISE_CLASS)); } -typedef struct _Efl_Promise_All Efl_Promise_All; +typedef struct _Efl_Promise_Composite Efl_Promise_All; typedef struct _Efl_Future_All Efl_Future_All; typedef struct _Efl_Accessor_All Efl_Accessor_All; +typedef struct _Efl_Promise_Composite Efl_Promise_Race; + struct _Efl_Accessor_All { Eina_Accessor accessor; @@ -607,17 +609,34 @@ struct _Efl_Future_All Efl_Promise_Msg *d; }; -struct _Efl_Promise_All +struct _Efl_Promise_Composite { Eina_Array members; Efl_Promise *promise; + Efl_Future *(*future_get)(void *item); + Eina_Bool failed : 1; Eina_Bool progress_triggered : 1; Eina_Bool future_triggered : 1; + Eina_Bool done : 1; }; +static Efl_Future * +_efl_promise_all_future_get(void *item) +{ + Efl_Future_All *fa = item; + + return fa->f; +} + +static Efl_Future * +_efl_promise_race_future_get(void *item) +{ + return item; +} + static void _efl_promise_all_free(Efl_Promise_All *all) { @@ -643,10 +662,19 @@ _efl_promise_all_die(void *data, const Efl_Event *ev EINA_UNUSED) } static void -_efl_all_future_set(void *data, const Eo_Event *ev EINA_UNUSED) +_efl_promise_race_die(void *data, const Efl_Event *ev EINA_UNUSED) { Efl_Promise_All *all = data; - Efl_Future_All *fa; + + eina_array_flush(&all->members); + free(all); +} + +static void +_efl_future_set(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Efl_Promise_All *all = data; + void *item; Eina_Array_Iterator iterator; unsigned int i; @@ -654,15 +682,17 @@ _efl_all_future_set(void *data, const Eo_Event *ev EINA_UNUSED) all->future_triggered = EINA_TRUE; // Propagate set on demand - EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator) + EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator) { - if (fa->f) + Efl_Future *f = all->future_get(item); + + if (f) { - Efl_Loop_Future_Data *pd = efl_data_scope_get(fa->f, EFL_LOOP_FUTURE_CLASS); + Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS); if (!pd->promise->set.future && !pd->promise->set.future_triggered) { - efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_SET, fa->f); + efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_SET, f); pd->promise->set.future_triggered = EINA_TRUE; pd->promise->set.future = EINA_TRUE; } @@ -671,10 +701,10 @@ _efl_all_future_set(void *data, const Eo_Event *ev EINA_UNUSED) } static void -_efl_all_future_progress_set(void *data, const Eo_Event *ev EINA_UNUSED) +_efl_future_progress_set(void *data, const Efl_Event *ev EINA_UNUSED) { Efl_Promise_All *all = data; - Efl_Future_All *fa; + void *item; Eina_Array_Iterator iterator; unsigned int i; @@ -682,15 +712,17 @@ _efl_all_future_progress_set(void *data, const Eo_Event *ev EINA_UNUSED) all->progress_triggered = 1; // Propagate progress set - EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator) + EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator) { - if (fa->f) + Efl_Future *f = all->future_get(item); + + if (f) { - Efl_Loop_Future_Data *pd = efl_data_scope_get(fa->f, EFL_LOOP_FUTURE_CLASS); + Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS); if (!pd->promise->set.progress && !pd->promise->set.progress_triggered) { - efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, fa->f); + efl_event_callback_call(pd->promise->promise, EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, f); pd->promise->set.progress_triggered = EINA_TRUE; pd->promise->set.progress = EINA_TRUE; } @@ -712,7 +744,28 @@ _efl_all_future_none(void *data, const Efl_Event *ev EINA_UNUSED) // Trigger cancel on all future EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator) { - if (!fa->d) efl_future_cancel(fa->f); + if (!fa->d && fa->f) efl_future_cancel(fa->f); + } + + // No one is listening to this promise anyway + _efl_promise_all_free(all); +} + +static void +_efl_race_future_none(void *data, const Efl_Event *ev EINA_UNUSED) +{ + Efl_Promise_Race *all = data; + Efl_Future *f; + Eina_Array_Iterator iterator; + unsigned int i; + + if (all->failed) return ; + all->failed = EINA_TRUE; + + // Trigger cancel on all future + EINA_ARRAY_ITER_NEXT(&all->members, i, f, iterator) + { + if (!f) efl_future_cancel(f); } // No one is listening to this promise anyway @@ -828,29 +881,28 @@ _progress(void *data, const Efl_Event *ev) { Efl_Promise_All *all = data; Efl_Future_Event_Progress *p = ev->info; - Efl_Future_All *fa; + void *item; Efl_Future_All_Progress a; Eina_Array_Iterator iterator; unsigned int i; a.inprogress = ev->object; a.progress = p->progress; - a.index = 0; - EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator) + EINA_ARRAY_ITER_NEXT(&all->members, i, item, iterator) { - if (fa->f == a.inprogress) - { - break ; - } - a.index++; + Efl_Future *f = all->future_get(item); + + if (f == a.inprogress) break ; } + a.index = i; + efl_promise_progress_set(all->promise, &a); } EFL_CALLBACKS_ARRAY_DEFINE(efl_all_callbacks, - { EFL_PROMISE_EVENT_FUTURE_SET, _efl_all_future_set }, - { EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, _efl_all_future_progress_set }, + { EFL_PROMISE_EVENT_FUTURE_SET, _efl_future_set }, + { EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, _efl_future_progress_set }, { EFL_PROMISE_EVENT_FUTURE_NONE, _efl_all_future_none }, { EFL_EVENT_DEL, _efl_promise_all_die }); @@ -964,4 +1016,147 @@ efl_future_iterator_all(Eina_Iterator *it) return NULL; } +static void +_then_race(void *data, const Efl_Event *ev) +{ + Efl_Future_Event_Success *success = ev->info; + Efl_Promise_Race *race = data; + // This is a trick due to the fact we are using internal function call to register this functions + Efl_Promise_Msg *d = success->value; + Efl_Future *f; + Eina_Array_Iterator iterator; + unsigned int i; + + if (race->done) return ; + race->done = EINA_TRUE; + + efl_ref(race->promise); + + EINA_ARRAY_ITER_NEXT(&race->members, i, f, iterator) + { + // To avoid double cancel/success + eina_array_data_set(&race->members, i, NULL); + + if (f == ev->object) + { + Efl_Future_Race_Success *success = calloc(1, sizeof (Efl_Future_Race_Success)); + + if (!success) continue ; + success->winner = f; + success->value = d->value; + success->index = i; + + efl_promise_value_set(race->promise, success, free); + } + else + { + efl_future_cancel(f); + } + } + + _efl_promise_all_free(race); + efl_unref(race->promise); +} + +static void +_fail_race(void *data, const Efl_Event *ev) +{ + Efl_Future_Event_Failure *fail = ev->info; + Efl_Promise_Race *race = data; + Efl_Future *f; + Eina_Array_Iterator iterator; + unsigned int i; + + if (race->done) return ; + race->done = EINA_TRUE; + + efl_ref(race->promise); + + EINA_ARRAY_ITER_NEXT(&race->members, i, f, iterator) + { + // To avoid double cancel/success + eina_array_data_set(&race->members, i, NULL); + + if (f != ev->object) + { + efl_future_cancel(f); + } + } + + efl_promise_failed(race->promise, fail->error); + _efl_promise_all_free(race); + efl_unref(race->promise); +} + +EFL_CALLBACKS_ARRAY_DEFINE(efl_race_callbacks, + { EFL_PROMISE_EVENT_FUTURE_SET, _efl_future_set }, + { EFL_PROMISE_EVENT_FUTURE_PROGRESS_SET, _efl_future_progress_set }, + { EFL_PROMISE_EVENT_FUTURE_NONE, _efl_race_future_none }, + { EFL_EVENT_DEL, _efl_promise_race_die }); + +static Efl_Promise_Race * +_efl_future_race_new(Eo *provider) +{ + Efl_Promise_Race *race; + Eo *loop; + + loop = efl_provider_find(provider, EFL_LOOP_CLASS); + if (!loop) return NULL; + + race = calloc(1, sizeof (Efl_Promise_Race)); + if (!race) return NULL; + + eina_array_step_set(&race->members, sizeof (Eina_Array), 8); + race->future_get = _efl_promise_race_future_get; + race->promise = efl_add(EFL_PROMISE_CLASS, loop); + if (!race->promise) goto on_error; + + return race; + + on_error: + free(race); + return NULL; +} + +static inline Efl_Future * +_efl_future_race_done(Efl_Promise_Race *race) +{ + Efl_Future *fn; + Eina_Array_Iterator iterator; + unsigned int i; + + EINA_ARRAY_ITER_NEXT(&race->members, i, fn, iterator) + _efl_loop_future_internal_then(fn, _then_race, _fail_race, _progress, race); + + efl_event_callback_array_add(race->promise, efl_race_callbacks(), race); + + return efl_promise_future_get(race->promise); +} + +EAPI Efl_Future * +efl_future_race_internal(Efl_Future *f1, ...) +{ + Efl_Promise_Race *race; + Efl_Future *fn; + va_list args; + + if (!f1) return NULL; + + race = _efl_future_race_new(f1); + if (!race) return NULL; + + eina_array_push(&race->members, f1); + + va_start(args, f1); + + while ((fn = va_arg(args, Efl_Future *))) + { + eina_array_push(&race->members, fn); + } + + + + return _efl_future_race_done(race); +} + #include "efl_promise.eo.c" --