ecore: add efl_future_all.
authorCedric Bail <cedric@osg.samsung.com>
Tue, 26 Jul 2016 19:08:30 +0000 (12:08 -0700)
committerCedric BAIL <cedric@osg.samsung.com>
Thu, 8 Sep 2016 21:58:05 +0000 (14:58 -0700)
src/lib/ecore/Ecore_Eo.h
src/lib/ecore/efl_promise.c

index 7e10c51..ef2b17f 100644 (file)
@@ -59,6 +59,20 @@ extern "C" {
 /* We ue the factory pattern here, so you shouldn't call eo_add directly. */
 EAPI Eo *ecore_main_loop_get(void);
 
+typedef struct _Efl_Future_Composite_Progress Efl_Future_All_Progress;
+
+struct _Efl_Future_Composite_Progress
+{
+   Efl_Future *inprogress;
+   void *progress;
+
+   unsigned int index;
+};
+
+EAPI Efl_Future *efl_future_all_internal(Efl_Future *f1, ...);
+
+#define efl_future_all(...) efl_future_all_internal(__VA_ARGS__, NULL)
+
 /**
  * @}
  */
index 38e1f2f..2bdf219 100644 (file)
@@ -97,6 +97,13 @@ _efl_loop_future_success(Efl_Event *ev, Efl_Loop_Future_Data *pd, void *value)
 
              cb->success((void*) cb->data, ev);
           }
+        else
+          {
+             chain_success.next = NULL;
+             chain_success.value = pd->message;
+             cb->success((void*) cb->data, ev);
+             chain_success.value = value;
+          }
 
         pd->callbacks = eina_inlist_remove(pd->callbacks, pd->callbacks);
         free(cb);
@@ -116,12 +123,9 @@ _efl_loop_future_failure(Efl_Event *ev, Efl_Loop_Future_Data *pd, Eina_Error err
 
    EINA_INLIST_FREE(pd->callbacks, cb)
      {
-        if (cb->next)
-          {
-             chain_fail.next = cb->next;
+        chain_fail.next = cb->next;
 
-             cb->failure((void*) cb->data, ev);
-          }
+        cb->failure((void*) cb->data, ev);
 
         pd->callbacks = eina_inlist_remove(pd->callbacks, pd->callbacks);
         free(cb);
@@ -173,7 +177,7 @@ _efl_loop_future_fulfilling(Eo *obj, Efl_Loop_Future_Data *pd)
 }
 
 static void
-_efl_loop_future_prepare_events(Efl_Loop_Future_Data *pd, Eina_Bool progress)
+_efl_loop_future_prepare_events(Efl_Loop_Future_Data *pd, Eina_Bool progress, Eina_Bool optional)
 {
    if (!pd->promise) return ;
 
@@ -183,17 +187,19 @@ _efl_loop_future_prepare_events(Efl_Loop_Future_Data *pd, Eina_Bool progress)
         ecore_loop_future_unregister(efl_provider_find(pd->self, EFL_LOOP_CLASS), pd->self);
      }
 
-   pd->promise->set.future = EINA_TRUE;
-   if (progress)
-     pd->promise->set.progress = EINA_TRUE;
+   if (!optional)
+     {
+        pd->promise->set.future = EINA_TRUE;
+        if (progress)
+          pd->promise->set.progress = EINA_TRUE;
+     }
 }
 
-static Efl_Future *
-_efl_loop_future_then(Eo *obj, Efl_Loop_Future_Data *pd,
-                      Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
+static Efl_Loop_Future_Callback *
+_efl_loop_future_then_internal(Efl_Loop_Future_Data *pd,
+                               Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
 {
    Efl_Loop_Future_Callback *cb;
-   Efl_Future *f;
 
    cb = calloc(1, sizeof (Efl_Loop_Future_Callback));
    if (!cb) return NULL;
@@ -202,18 +208,46 @@ _efl_loop_future_then(Eo *obj, Efl_Loop_Future_Data *pd,
    cb->failure = failure;
    cb->progress = progress;
    cb->data = data;
-   cb->next = efl_add(EFL_PROMISE_CLASS, obj);
+   pd->callbacks = eina_inlist_append(pd->callbacks, EINA_INLIST_GET(cb));
 
-   f = efl_promise_future_get(cb->next);
+   return cb;
+}
 
-   pd->callbacks = eina_inlist_append(pd->callbacks, EINA_INLIST_GET(cb));
+static Efl_Future *
+_efl_loop_future_then(Eo *obj, Efl_Loop_Future_Data *pd,
+                      Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
+{
+   Efl_Loop_Future_Callback *cb;
+   Efl_Future *f;
 
-   _efl_loop_future_prepare_events(pd, !!progress);
+   cb = _efl_loop_future_then_internal(pd, success, failure, progress, data);
+   if (!cb) return NULL;
+
+   cb->next = efl_add(EFL_PROMISE_CLASS, obj);
+   f = efl_promise_future_get(cb->next);
+
+   _efl_loop_future_prepare_events(pd, !!progress, EINA_FALSE);
    _efl_loop_future_fulfilling(obj, pd);
 
    return f;
 }
 
+static Eina_Bool
+_efl_loop_future_internal_then(Efl_Future *f,
+                               Efl_Event_Cb success, Efl_Event_Cb failure, Efl_Event_Cb progress, const void *data)
+{
+   Efl_Loop_Future_Data *pd = efl_data_scope_get(f, EFL_LOOP_FUTURE_CLASS);
+   Efl_Loop_Future_Callback *cb;
+
+   cb = _efl_loop_future_then_internal(pd, success, failure, progress, data);
+   if (!cb) return EINA_FALSE;
+
+   _efl_loop_future_prepare_events(pd, !!progress, EINA_TRUE);
+   _efl_loop_future_fulfilling(f, pd);
+
+   return EINA_FALSE;
+}
+
 static void
 _efl_loop_future_disconnect(Eo *obj, Efl_Loop_Future_Data *pd)
 {
@@ -554,4 +588,357 @@ _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_Future_All Efl_Future_All;
+typedef struct _Efl_Accessor_All Efl_Accessor_All;
+
+struct _Efl_Accessor_All
+{
+   Eina_Accessor accessor;
+
+   Efl_Promise_All *all;
+   Efl_Promise *promise;
+};
+
+struct _Efl_Future_All
+{
+   Efl_Future *f;
+
+   Efl_Promise_Msg *d;
+};
+
+struct _Efl_Promise_All
+{
+   Eina_Array members;
+
+   Efl_Promise *promise;
+
+   Eina_Bool failed : 1;
+   Eina_Bool progress_triggered : 1;
+   Eina_Bool future_triggered : 1;
+};
+
+static void
+_efl_promise_all_free(Efl_Promise_All *all)
+{
+   efl_del(all->promise);
+   all->promise = NULL;
+}
+
+static void
+_efl_promise_all_die(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+
+   while ((fa = eina_array_pop(&all->members)))
+     {
+        EINA_REFCOUNT_UNREF(fa->d)
+          _efl_promise_msg_free(fa->d);
+        assert(fa->f == NULL);
+        free(fa);
+     }
+   eina_array_flush(&all->members);
+   free(all);
+}
+
+static void
+_efl_all_future_set(void *data, const Eo_Event *ev EINA_UNUSED)
+{
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   if (all->future_triggered) return ;
+   all->future_triggered = EINA_TRUE;
+
+   // Propagate set on demand
+   EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
+     {
+        if (fa->f)
+          {
+             Efl_Loop_Future_Data *pd = efl_data_scope_get(fa->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);
+                  pd->promise->set.future_triggered = EINA_TRUE;
+                  pd->promise->set.future = EINA_TRUE;
+               }
+          }
+     }
+}
+
+static void
+_efl_all_future_progress_set(void *data, const Eo_Event *ev EINA_UNUSED)
+{
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   if (all->progress_triggered) return ;
+   all->progress_triggered = 1;
+
+   // Propagate progress set
+   EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
+     {
+        if (fa->f)
+          {
+             Efl_Loop_Future_Data *pd = efl_data_scope_get(fa->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);
+                  pd->promise->set.progress_triggered = EINA_TRUE;
+                  pd->promise->set.progress = EINA_TRUE;
+               }
+          }
+     }
+}
+
+static void
+_efl_all_future_none(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+   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, fa, iterator)
+     {
+        if (!fa->d) efl_future_cancel(fa->f);
+     }
+
+   // No one is listening to this promise anyway
+   _efl_promise_all_free(all);
+}
+
+static Eina_Bool
+_efl_accessor_all_get_at(Efl_Accessor_All *ac, unsigned int pos, void **data)
+{
+   Efl_Future_All *fa;
+
+   fa = eina_array_data_get(&ac->all->members, pos);
+   if (!fa) return EINA_FALSE;
+
+   *data = fa->d->value;
+   return EINA_TRUE;
+}
+
+static Efl_Promise *
+_efl_accessor_all_get_container(Efl_Accessor_All *ac)
+{
+   return ac->all->promise;
+}
+
+static void
+_efl_accessor_all_free(Efl_Accessor_All *ac)
+{
+   _efl_promise_all_free(ac->all);
+   efl_unref(ac->promise);
+   free(ac);
+}
+
+static void
+_then_all(void *data, const Efl_Event *ev)
+{
+   Efl_Future_Event_Success *success = ev->info;
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+   // This is a trick due to the fact we are using internal function call to register this functions
+   Efl_Promise_Msg *d = success->value;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+   Eina_Bool done = EINA_TRUE;
+
+   // Only when all value are received can we propagate the success
+   EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
+     {
+        if (fa->f == ev->object)
+          {
+             fa->d = d;
+             EINA_REFCOUNT_REF(fa->d);
+          }
+        done &= !!fa->d;
+     }
+
+   if (done)
+     {
+        Efl_Accessor_All *ac;
+
+        ac = calloc(1, sizeof (Efl_Accessor_All));
+        if (!ac) return ; // We do now the promise and all here
+
+        EINA_MAGIC_SET(&ac->accessor, EINA_MAGIC_ACCESSOR);
+
+        ac->accessor.version = EINA_ACCESSOR_VERSION;
+        ac->accessor.get_at = FUNC_ACCESSOR_GET_AT(_efl_accessor_all_get_at);
+        ac->accessor.get_container = FUNC_ACCESSOR_GET_CONTAINER(_efl_accessor_all_get_container);
+        ac->accessor.free = FUNC_ACCESSOR_FREE(_efl_accessor_all_free);
+        ac->all = all;
+        ac->promise = efl_ref(all->promise);
+
+        efl_promise_value_set(all->promise, &ac->accessor, EINA_FREE_CB(eina_accessor_free));
+     }
+}
+
+static void
+_fail_all(void *data, const Efl_Event *ev)
+{
+   Efl_Future_Event_Failure *fail = ev->info;
+   Efl_Promise_All *all = data;
+   Efl_Future_All *fa;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   if (all->failed) return ;
+   all->failed = EINA_TRUE;
+
+   efl_ref(all->promise);
+
+   // In case of one fail, the entire promise will fail and
+   // all remaining future will be cancelled
+   EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
+     {
+        if (fa->d)
+          {
+             EINA_REFCOUNT_UNREF(fa->d)
+               _efl_promise_msg_free(fa->d);
+             fa->d = NULL;
+          }
+        else
+          {
+             efl_future_cancel(fa->f);
+          }
+     }
+
+   efl_promise_failed(all->promise, fail->error);
+   _efl_promise_all_free(all);
+   efl_unref(all->promise);
+}
+
+static void
+_progress(void *data, const Efl_Event *ev)
+{
+   Efl_Promise_All *all = data;
+   Efl_Future_Event_Progress *p = ev->info;
+   Efl_Future_All *fa;
+   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)
+     {
+        if (fa->f == a.inprogress)
+          {
+             break ;
+          }
+        a.index++;
+     }
+   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_NONE, _efl_all_future_none },
+                           { EFL_EVENT_DEL, _efl_promise_all_die });
+
+static inline Efl_Promise_All *
+_efl_future_all_new(Eo *provider)
+{
+   Efl_Promise_All *all;
+   Eo *loop;
+
+   loop = efl_provider_find(provider, EFL_LOOP_CLASS);
+   if (!loop) return NULL;
+
+   all = calloc(1, sizeof (Efl_Promise_All));
+   if (!all) return NULL;
+
+   eina_array_step_set(&all->members, sizeof (Eina_Array), 8);
+   all->future_get = _efl_promise_all_future_get;
+   all->promise = efl_add(EFL_PROMISE_CLASS, NULL);
+   if (!all->promise) goto on_error;
+
+   return all;
+
+ on_error:
+   free(all);
+   return NULL;
+}
+
+static inline Efl_Future *
+_efl_future_all_done(Efl_Promise_All *all)
+{
+   Efl_Future_All *fa;
+   Eina_Array_Iterator iterator;
+   unsigned int i;
+
+   EINA_ARRAY_ITER_NEXT(&all->members, i, fa, iterator)
+     _efl_loop_future_internal_then(fa->f, _then_all, _fail_all, _progress, all);
+
+   efl_event_callback_array_add(all->promise, efl_all_callbacks(), all);
+
+   return efl_promise_future_get(all->promise);
+}
+
+static Eina_Bool
+_efl_future_all_append(Efl_Promise_All *all, Efl_Future *fn)
+{
+   Efl_Future_All *fa;
+
+   fa = calloc(1, sizeof (Efl_Future_All));
+   if (!fa) return EINA_FALSE;
+   efl_future_use(&fa->f, fn);
+   eina_array_push(&all->members, fa);
+
+   return EINA_TRUE;
+}
+
+EAPI Efl_Future *
+efl_future_all_internal(Efl_Future *f1, ...)
+{
+   Efl_Promise_All *all;
+   Efl_Future *fn;
+   va_list args;
+
+   if (!f1) return NULL;
+
+   all = _efl_future_all_new(f1);
+   if (!all) return NULL;
+
+   if (!_efl_future_all_append(all, f1))
+     goto on_error;
+
+   va_start(args, f1);
+   while ((fn = va_arg(args, Efl_Future *)))
+     {
+        if (!_efl_future_all_append(all, fn))
+          {
+             va_end(args);
+             goto on_error;
+          }
+     }
+   va_end(args);
+
+   return _efl_future_all_done(all);
+
+
+
+ on_error:
+   _efl_promise_all_die(all, NULL);
+   return NULL;
+}
+
 #include "efl_promise.eo.c"