eina: Add eina_promise_race composition function
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Mon, 9 May 2016 22:10:26 +0000 (19:10 -0300)
committerFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Mon, 9 May 2016 22:10:26 +0000 (19:10 -0300)
Added eina_promise_race function that composes multiple
promise objects into a new promise which is fulfilled
when one of the promises are fulfilled, or fails
when one of the promises have failed.

src/lib/eina/eina_promise.c
src/lib/eina/eina_promise.h
src/tests/eina/eina_test_promise.c

index 2fe19a4..93e7094 100644 (file)
@@ -104,6 +104,19 @@ struct _Eina_Promise_Iterator
    } data;
 };
 
+typedef struct _Eina_Promise_Race_Value_Type _Eina_Promise_Race_Value_Type;
+struct _Eina_Promise_Race_Value_Type
+{
+   void* value;
+   unsigned int promise_index;
+   unsigned int num_promises;
+   struct _Eina_Promise_Race_Information
+   {
+      Eina_Promise* promise;
+      _Eina_Promise_Default_Owner* self;
+   } promises[];
+};
+
 static void _eina_promise_free_progress_callback_node(void* node)
 {
    _Eina_Promise_Progress_Cb *progress_cb = node;
@@ -669,6 +682,96 @@ eina_promise_progress_notification(Eina_Promise_Owner* promise)
   return eina_promise_owner_promise_get(owner);
 }
 
+// Race implementation
+static void
+_eina_promise_race_free(_Eina_Promise_Race_Value_Type* value)
+{
+   unsigned i = 0;
+   for (;i != value->num_promises; ++i)
+     {
+        eina_promise_unref(value->promises[i].promise);
+     }
+}
+
+static void
+_eina_promise_race_compose_then_cb(struct _Eina_Promise_Race_Information* info, void* value EINA_UNUSED)
+{
+   _Eina_Promise_Default_Owner* race_promise;
+   _Eina_Promise_Race_Value_Type *race_value;
+
+   race_promise = info->self;
+   race_value = (_Eina_Promise_Race_Value_Type*)race_promise->value;
+
+   if (!race_promise->promise.has_finished)
+     {
+        race_value->value = value;
+        race_value->promise_index =  info - &race_value->promises[0];
+        _eina_promise_finish(race_promise);
+     }
+}
+
+static void
+_eina_promise_race_compose_error_then_cb(struct _Eina_Promise_Race_Information* info, Eina_Error const* error)
+{
+   _Eina_Promise_Default_Owner* race_promise;
+   _Eina_Promise_Race_Value_Type *race_value;
+
+   race_promise = info->self;
+   race_value = (_Eina_Promise_Race_Value_Type*)race_promise->value;
+
+   if (!race_promise->promise.has_finished)
+     {
+        race_value->promise_index =  info - &race_value->promises[0];
+        eina_promise_owner_error_set(&race_promise->owner_vtable, *error);
+     }
+}
+
+Eina_Promise *
+eina_promise_race(Eina_Iterator* it)
+{
+   _Eina_Promise_Default_Owner *promise;
+   Eina_Promise* current;
+   Eina_Array* promises;
+   struct _Eina_Promise_Race_Information *cur_promise, *last;
+   _Eina_Promise_Race_Value_Type *value;
+   int num_promises;
+
+   promises = eina_array_new(20);
+
+   EINA_ITERATOR_FOREACH(it, current)
+     {
+        eina_array_push(promises, current);
+     }
+
+   eina_iterator_free(it);
+
+   num_promises = eina_array_count_get(promises);
+   promise = (_Eina_Promise_Default_Owner*)
+     eina_promise_default_add(sizeof(_Eina_Promise_Race_Value_Type) +
+                              sizeof(struct _Eina_Promise_Race_Information*)*num_promises);
+   value = eina_promise_owner_buffer_get((Eina_Promise_Owner*)promise);
+   value->value = NULL;
+   value->promise_index = -1;
+   value->num_promises = num_promises;
+
+   promise->promise.value_free_cb = (Eina_Promise_Free_Cb)&_eina_promise_race_free;
+
+   cur_promise = value->promises;
+   last = value->promises + value->num_promises;
+   for (int i = 0;cur_promise != last; ++cur_promise, ++i)
+     {
+        cur_promise->promise = eina_array_data_get(promises, i);
+        cur_promise->self = promise;
+        eina_promise_then(cur_promise->promise, (Eina_Promise_Cb)&_eina_promise_race_compose_then_cb,
+                          (Eina_Promise_Error_Cb)&_eina_promise_race_compose_error_then_cb, cur_promise);
+        eina_promise_ref(cur_promise->promise); // We need to keep the value alive until this promise is freed
+     }
+
+   eina_array_free(promises);
+
+   return &promise->promise.vtable;
+}
+
 // API functions
 EAPI void
 eina_promise_then(Eina_Promise* promise, Eina_Promise_Cb callback,
index 51bdb80..916d6ea 100644 (file)
@@ -224,7 +224,11 @@ EAPI void eina_promise_then(Eina_Promise* promise, Eina_Promise_Cb callback,
                             Eina_Promise_Error_Cb error_cb, void* data);
 
 /*
- * @brief Creates a new Eina_Promise from other Eina_Promises
+ * @brief Creates a new @Eina_Promise from other @Eina_Promise's
+ *
+ * The new @Eina_Promise is fulfilled when all promises
+ * have also been fulfilled with success or when the
+ * first one fails
  *
  * @param promises An Eina_Iterator for all Eina_Promises
  * @return Returns a new Eina_Promise
@@ -232,6 +236,18 @@ EAPI void eina_promise_then(Eina_Promise* promise, Eina_Promise_Cb callback,
 EAPI Eina_Promise* eina_promise_all(Eina_Iterator* promises);
 
 /*
+ * @brief Creates a new @Eina_Promise from other @Eina_Promises
+ *
+ * The new @Eina_Promise is fulfilled when the first promise
+ * has been fulfilled with success or when the
+ * first one fails
+ *
+ * @param promises An Eina_Iterator for all Eina_Promises
+ * @return Returns a new Eina_Promise
+ */
+EAPI Eina_Promise* eina_promise_race(Eina_Iterator* promises);
+
+/*
  * @brief Creates a new @Eina_Promise from another @Eina_Promise_Owner which
  * is fulfilled when @promise has a progress callback registered
  *
index 59199e1..2932e1c 100644 (file)
@@ -336,6 +336,28 @@ START_TEST(eina_test_promise_ignored)
 }
 END_TEST
 
+START_TEST(eina_test_promise_race)
+{
+   Eina_Promise_Owner* promise_owner;
+   Eina_Promise* first[2] = {NULL, NULL};
+   Eina_Promise* promise;
+   Eina_Bool ran = EINA_FALSE;
+
+   eina_init();
+
+   promise_owner = eina_promise_default_add(0);
+   first[0] = eina_promise_owner_promise_get(promise_owner);
+   promise = eina_promise_race(eina_carray_iterator_new((void**)&first[0]));
+
+   eina_promise_then(promise, &_eina_test_promise_cb, NULL, &ran);
+   eina_promise_owner_value_set(promise_owner, NULL, NULL);
+
+   ck_assert(ran == EINA_TRUE);
+
+   eina_shutdown();
+}
+END_TEST
+
 void
 eina_test_promise(TCase *tc)
 {
@@ -350,4 +372,5 @@ eina_test_promise(TCase *tc)
    tcase_add_test(tc, eina_test_promise_progress_notify2);
    tcase_add_test(tc, eina_test_promise_progress_notify3);
    tcase_add_test(tc, eina_test_promise_ignored);
+   tcase_add_test(tc, eina_test_promise_race);
 }