eina: Add eina_promise_race composition function

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.
This commit is contained in:
Felipe Magno de Almeida 2016-05-09 19:10:26 -03:00
parent d734cd4cad
commit 1c5ce16565
3 changed files with 143 additions and 1 deletions

View 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,

View File

@ -224,13 +224,29 @@ 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
*/
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

View 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);
}