diff --git a/src/lib/eina/eina_promise.c b/src/lib/eina/eina_promise.c index 2fe19a4bc1..93e70948e5 100644 --- a/src/lib/eina/eina_promise.c +++ b/src/lib/eina/eina_promise.c @@ -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, diff --git a/src/lib/eina/eina_promise.h b/src/lib/eina/eina_promise.h index 51bdb80884..916d6eab7d 100644 --- a/src/lib/eina/eina_promise.h +++ b/src/lib/eina/eina_promise.h @@ -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 diff --git a/src/tests/eina/eina_test_promise.c b/src/tests/eina/eina_test_promise.c index 59199e1420..2932e1c792 100644 --- a/src/tests/eina/eina_test_promise.c +++ b/src/tests/eina/eina_test_promise.c @@ -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); }