felipealmeida pushed a commit to branch master. http://git.enlightenment.org/core/efl.git/commit/?id=f9ba80ab33e0b94dad7ec103e6d261a644f7835f
commit f9ba80ab33e0b94dad7ec103e6d261a644f7835f Author: Felipe Magno de Almeida <fel...@expertisesolutions.com.br> Date: Sun Mar 6 17:39:20 2016 -0300 ecore: Create Promises Add a promise object that allows Eolian interface to include promises as a way to have asynchronous value return and composibility. The usage is like this in a .eo file: class Foo { methods { bar { params { promise: Promise<int>; } } } } Which will create the following API interface: void foo_bar(Ecore_Promise** promise); and the equivalent declaration for implementation. However, the API function will instantiate the Promise for the user and the implementer of the class. --- src/Makefile_Ecore.am | 5 +- src/bin/eolian/eo_generator.c | 30 ++- src/lib/ecore/Ecore.h | 1 + src/lib/ecore/ecore_promise.c | 452 +++++++++++++++++++++++++++++++++++ src/lib/ecore/ecore_promise.h | 136 +++++++++++ src/lib/eo/Eo.h | 63 +++-- src/lib/eolian/eo_lexer.c | 3 +- src/lib/eolian/eo_lexer.h | 6 +- src/lib/eolian/eo_parser.c | 2 +- src/tests/ecore/ecore_suite.c | 1 + src/tests/ecore/ecore_suite.h | 1 + src/tests/ecore/ecore_test_promise.c | 364 ++++++++++++++++++++++++++++ 12 files changed, 1042 insertions(+), 22 deletions(-) diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am index 49936af..79ac16c 100644 --- a/src/Makefile_Ecore.am +++ b/src/Makefile_Ecore.am @@ -47,7 +47,8 @@ lib/ecore/Ecore.h \ lib/ecore/Ecore_Common.h \ lib/ecore/Ecore_Legacy.h \ lib/ecore/Ecore_Eo.h \ -lib/ecore/Ecore_Getopt.h +lib/ecore/Ecore_Getopt.h \ +lib/ecore/ecore_promise.h nodist_installed_ecoremainheaders_DATA = \ $(ecore_eolian_h) @@ -72,6 +73,7 @@ lib/ecore/ecore_timer.c \ lib/ecore/ecore_thread.c \ lib/ecore/ecore_throttle.c \ lib/ecore/ecore_exe.c \ +lib/ecore/ecore_promise.c \ lib/ecore/ecore_exe_private.h \ lib/ecore/ecore_private.h @@ -199,6 +201,7 @@ tests/ecore/ecore_test_animator.c \ tests/ecore/ecore_test_ecore_thread_eina_thread_queue.c \ tests/ecore/ecore_test_ecore_input.c \ tests/ecore/ecore_test_ecore_file.c \ +tests/ecore/ecore_test_promise.c \ tests/ecore/ecore_suite.h tests_ecore_ecore_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ diff --git a/src/bin/eolian/eo_generator.c b/src/bin/eolian/eo_generator.c index a97f2f0..4810658 100644 --- a/src/bin/eolian/eo_generator.c +++ b/src/bin/eolian/eo_generator.c @@ -311,6 +311,9 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid, if (ftype != EOLIAN_PROP_GET && ftype != EOLIAN_PROP_SET) ftype = eolian_function_type_get(funcid); Eina_Bool is_prop = (ftype == EOLIAN_PROP_GET || ftype == EOLIAN_PROP_SET); + Eina_Bool has_promise = EINA_FALSE; + const char* promise_param_name = NULL; + const char* promise_value_type = NULL; Eina_Bool need_implementation = EINA_TRUE; if (!impl_env && eolian_function_is_virtual_pure(funcid, ftype)) need_implementation = EINA_FALSE; @@ -337,9 +340,11 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid, if (eina_iterator_next(itr, &data) && !eina_iterator_next(itr, &data2)) { Eolian_Function_Parameter *param = data; + const char* rettype_str = NULL; rettypet = eolian_parameter_type_get(param); var_as_ret = EINA_TRUE; default_ret_val = eolian_parameter_default_value_get(param); + eina_stringshare_del(rettype_str); } eina_iterator_free(itr); } @@ -375,9 +380,24 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid, const char *ptype = eolian_type_c_type_get(ptypet); Eolian_Parameter_Dir pdir = eolian_parameter_direction_get(param); Eina_Bool had_star = !!strchr(ptype, '*'); + + if(!has_promise && !strcmp(ptype, "Ecore_Promise *")) + { + Eina_Iterator* promise_values; + has_promise = EINA_TRUE; + promise_param_name = eina_stringshare_add(pname); + promise_values = eolian_type_subtypes_get(eolian_type_base_type_get(ptypet)); + Eolian_Type* subtype; + if(eina_iterator_next(promise_values, (void**)&subtype)) + promise_value_type = eolian_type_c_type_get(subtype); + } + if (ftype == EOLIAN_UNRESOLVED || ftype == EOLIAN_METHOD) add_star = (pdir == EOLIAN_OUT_PARAM || pdir == EOLIAN_INOUT_PARAM); if (eina_strbuf_length_get(params)) eina_strbuf_append(params, ", "); - eina_strbuf_append_printf(params, "%s", pname); + if(has_promise) + eina_strbuf_append_printf(params, "%s", "&__eo_promise"); + else + eina_strbuf_append_printf(params, "%s", pname); eina_strbuf_append_printf(full_params, ", %s%s%s%s%s", ptype, had_star?"":" ", add_star?"*":"", pname, is_empty && !dflt_value ?" EINA_UNUSED":""); if (is_auto) @@ -511,8 +531,8 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid, Eina_Bool ret_is_void = (!rettype || !strcmp(rettype, "void")); _class_func_env_create(class, eolian_function_name_get(funcid), ftype, &func_env); eina_strbuf_append_printf(eo_func_decl, - "EOAPI EO_%sFUNC_BODY%s%s(%s", - ret_is_void?"VOID_":"", has_params?"V":"", + "EOAPI EO_%sFUNC_%sBODY%s%s(%s", + ret_is_void?"VOID_":"", has_promise?"PROMISE_":"", has_params?"V":"", (ftype == EOLIAN_PROP_GET || eolian_function_object_is_const(funcid) || eolian_function_is_class(funcid))?"_CONST":"", func_env.lower_eo_func); @@ -535,6 +555,10 @@ eo_bind_func_generate(const Eolian_Class *class, const Eolian_Function *funcid, eina_stringshare_del(string); } } + if (has_promise) + { + eina_strbuf_append_printf(eo_func_decl, ", %s, %s", promise_param_name, promise_value_type); + } if (has_params) { eina_strbuf_replace_all(full_params, " EINA_UNUSED", ""); diff --git a/src/lib/ecore/Ecore.h b/src/lib/ecore/Ecore.h index e843038..df28b64 100644 --- a/src/lib/ecore/Ecore.h +++ b/src/lib/ecore/Ecore.h @@ -356,6 +356,7 @@ extern "C" { #endif #ifdef EFL_EO_API_SUPPORT #include "Ecore_Eo.h" +#include "ecore_promise.h" #endif #ifdef __cplusplus diff --git a/src/lib/ecore/ecore_promise.c b/src/lib/ecore/ecore_promise.c new file mode 100644 index 0000000..c55d519 --- /dev/null +++ b/src/lib/ecore/ecore_promise.c @@ -0,0 +1,452 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <Eina.h> +#include <Ecore.h> + +#include <assert.h> + +typedef void(*Ecore_Promise_Free_Cb)(void*); + +struct _Ecore_Promise_Then_Cb +{ + EINA_INLIST; + + Ecore_Promise_Cb callback; + void* data; +}; + +struct _Ecore_Promise +{ + Eina_Lock lock; + Eina_Condition condition; + Eina_Bool has_finished : 1; + Eina_Bool has_errored : 1; + Eina_Bool has_pending_call : 1; + Eina_Bool is_then_calls_manual : 1; + Eina_Error error; + size_t value_size; + int ref; + + struct _Ecore_Promise_Then_Cb then_callbacks; + Ecore_Promise_Free_Cb free_cb; + + char value[]; +}; + +struct _Ecore_Promise_Thread_Data +{ + const void* data; + Ecore_Promise_Thread_Cb func_blocking; + Ecore_Promise* promise; +}; + +typedef struct _Ecore_Promise_Iterator _Ecore_Promise_Iterator; +typedef struct _Ecore_Promise_Success_Iterator _Ecore_Promise_Success_Iterator; +typedef struct _Ecore_Promise_Failure_Iterator _Ecore_Promise_Failure_Iterator; +struct _Ecore_Promise_Iterator +{ + Eina_Iterator* success_iterator; + Eina_Iterator* failure_iterator; + struct _Ecore_Promise_Success_Iterator + { + Eina_Iterator success_iterator_impl; + struct _Ecore_Promise_Failure_Iterator + { + Eina_Iterator failure_iterator_impl; + unsigned int promise_index; + unsigned int num_promises; + unsigned int promises_finished; + Ecore_Promise* promises[]; + } data; + } data; +}; + +static void _ecore_promise_lock_take(Ecore_Promise* promise); +static void _ecore_promise_lock_release(Ecore_Promise* promise); +static void _ecore_promise_finish(Ecore_Promise* promise); +static void _ecore_promise_then_calls(Ecore_Promise* promise); +static void _ecore_promise_unsafe_ref(Ecore_Promise const* promise); +static void _ecore_promise_unsafe_unref(Ecore_Promise const* promise); +static void _ecore_promise_unsafe_free_unref(Ecore_Promise const* promise); +static Eina_Bool _ecore_promise_unlock_unsafe_free_unref(Ecore_Promise const* promise); + +static void _ecore_promise_iterator_setup(_Ecore_Promise_Iterator* iterator, Eina_Array* promises); + +static void _ecore_promise_thread_end(void* data, Ecore_Thread* thread EINA_UNUSED) +{ + struct _Ecore_Promise_Thread_Data* p = data; + _ecore_promise_lock_take(p->promise); + if(p->promise->has_finished) + { + p->promise->has_pending_call = EINA_FALSE; + if(!_ecore_promise_unlock_unsafe_free_unref(p->promise)) + _ecore_promise_then_calls(p->promise); + } + else + { + p->promise->is_then_calls_manual = EINA_FALSE; + p->promise->has_pending_call = EINA_FALSE; + _ecore_promise_unlock_unsafe_free_unref(p->promise); + } + free(data); +} + +static void +_ecore_promise_thread_blocking(void* data, Ecore_Thread* thread EINA_UNUSED) +{ + struct _Ecore_Promise_Thread_Data* p = data; + (p->func_blocking)(p->data, p->promise); +} + +static void +_ecore_promise_then_calls(Ecore_Promise* promise) +{ + _ecore_promise_lock_take(promise); + struct _Ecore_Promise_Then_Cb then_callbacks = promise->then_callbacks; + memset(&promise->then_callbacks, 0, sizeof(promise->then_callbacks)); + promise->has_pending_call = EINA_FALSE; + _ecore_promise_lock_release(promise); + + struct _Ecore_Promise_Then_Cb* callback; + + if(then_callbacks.callback) + { + (*then_callbacks.callback)(then_callbacks.data, &promise->value[0]); + _ecore_promise_unsafe_free_unref(promise); + } + + if(EINA_INLIST_GET(&then_callbacks)->next) + { + Eina_Inlist* list2; + EINA_INLIST_FOREACH_SAFE(EINA_INLIST_GET(&then_callbacks)->next, list2, callback) + { + if(callback->callback) + { + (*callback->callback)(callback->data, promise); + } + _ecore_promise_unsafe_free_unref(promise); + } + } +} + +Ecore_Promise* ecore_promise_thread_run(Ecore_Promise_Thread_Cb func_blocking, const void* data, size_t value_size) +{ + struct _Ecore_Promise_Thread_Data *new_data = malloc(sizeof(struct _Ecore_Promise_Thread_Data)); + new_data->data = data; + new_data->func_blocking = func_blocking; + new_data->promise = ecore_promise_add(value_size); + new_data->promise->is_then_calls_manual = EINA_TRUE; + new_data->promise->has_pending_call = EINA_TRUE; + ecore_promise_ref(new_data->promise); + ecore_thread_run(&_ecore_promise_thread_blocking, &_ecore_promise_thread_end, NULL, new_data); + return new_data->promise; +} + +Ecore_Promise* ecore_promise_add(int value_size) +{ + Ecore_Promise* p = malloc(sizeof(Ecore_Promise) + value_size); + eina_lock_new(&p->lock); + eina_condition_new(&p->condition, &p->lock); + p->has_finished = p->has_errored = p->has_pending_call = p->is_then_calls_manual = EINA_FALSE; + p->ref = 1; + memset(&p->then_callbacks, 0, sizeof(p->then_callbacks)); + p->value_size = value_size; + p->free_cb = NULL; + return p; +} + +static void _ecore_promise_del(Ecore_Promise* promise) +{ + if(promise->free_cb) + promise->free_cb((void*)&promise->value[0]); + + eina_lock_free(&promise->lock); + eina_condition_free(&promise->condition); +} + +void* ecore_promise_buffer_get(Ecore_Promise* promise) +{ + return &promise->value[0]; +} + +void* ecore_promise_value_get(Ecore_Promise const* promise) +{ + _ecore_promise_lock_take((Ecore_Promise*)promise); + _ecore_promise_unsafe_ref(promise); + while(!promise->has_finished) + { + eina_condition_wait((Eina_Condition*)&promise->condition); + } + + void* v = (void*)(promise->value_size && !promise->has_errored ? &promise->value[0] : NULL); + _ecore_promise_unsafe_unref(promise); + _ecore_promise_lock_release((Ecore_Promise*)promise); + return v; +} + +void ecore_promise_value_set(Ecore_Promise* promise, void* data) +{ + _ecore_promise_lock_take(promise); + if(data && promise->value_size) + { + memcpy(&promise->value[0], data, promise->value_size); + } + + _ecore_promise_finish(promise); +} + +static void _ecore_promise_all_compose_then_cb(Ecore_Promise* promise, void* value EINA_UNUSED) +{ + _ecore_promise_lock_take(promise); + _Ecore_Promise_Iterator* iterator = (_Ecore_Promise_Iterator*)promise->value; + + if(++iterator->data.data.promises_finished == iterator->data.data.num_promises) + { + _ecore_promise_finish(promise); + } + else + _ecore_promise_lock_release(promise); +} + +static void _ecore_promise_all_free(_Ecore_Promise_Iterator* value) +{ + unsigned i = 0; + eina_iterator_free(value->success_iterator); + /* eina_iterator_free(value->failure_iterator); */ + + for(;i != value->data.data.num_promises; ++i) + { + ecore_promise_unref(value->data.data.promises[i]); + } +} + +Ecore_Promise* ecore_promise_all(Eina_Iterator* it) +{ + Ecore_Promise* current, *promise; + Eina_Array* promises; + + promises = eina_array_new(20); + + EINA_ITERATOR_FOREACH(it, current) + { + eina_array_push(promises, current); + } + + promise = ecore_promise_add(sizeof(_Ecore_Promise_Iterator) + sizeof(Ecore_Promise*)*eina_array_count_get(promises)); + //promise->is_then_calls_manual = EINA_TRUE; + promise->free_cb = (Ecore_Promise_Free_Cb)_ecore_promise_all_free; + _Ecore_Promise_Iterator* internal_it = ecore_promise_buffer_get(promise); + _ecore_promise_iterator_setup(internal_it, promises); + eina_array_free(promises); + + { + Ecore_Promise** cur_promise = internal_it->data.data.promises, ** last = + internal_it->data.data.promises + internal_it->data.data.num_promises; + for(;cur_promise != last; ++cur_promise) + { + ecore_promise_ref(*cur_promise); + ecore_promise_then(*cur_promise, (Ecore_Promise_Cb)&_ecore_promise_all_compose_then_cb, promise); + } + } + + return promise; +} + +void ecore_promise_then(Ecore_Promise* promise, Ecore_Promise_Cb callback, void* data) +{ + _ecore_promise_lock_take(promise); + _ecore_promise_unsafe_ref(promise); + if(!promise->then_callbacks.callback && !EINA_INLIST_GET(&promise->then_callbacks)->next) + { + promise->then_callbacks.callback = callback; + promise->then_callbacks.data = data; + } + else + { + struct _Ecore_Promise_Then_Cb* p = malloc(sizeof(struct _Ecore_Promise_Then_Cb)); + p->callback = callback; + p->data = data; + Eina_Inlist* l = eina_inlist_append(EINA_INLIST_GET(&promise->then_callbacks), EINA_INLIST_GET(p)); + (void)l; + } + if(promise->has_finished && !promise->has_pending_call) + { + promise->has_pending_call = EINA_TRUE; + _ecore_promise_lock_release(promise); + ecore_job_add((Ecore_Cb)&_ecore_promise_then_calls, promise); + } + else + _ecore_promise_lock_release(promise); +} + +EAPI Eina_Error ecore_promise_error_get(Ecore_Promise const* promise) +{ + _ecore_promise_lock_take((Ecore_Promise*)promise); + if(promise->has_errored) + { + Eina_Error error = promise->error; + _ecore_promise_lock_release((Ecore_Promise*)promise); + return error; + } + else + { + _ecore_promise_lock_release((Ecore_Promise*)promise); + return 0; + } +} + +EAPI void ecore_promise_error_set(Ecore_Promise* promise, Eina_Error error) +{ + _ecore_promise_lock_take(promise); + promise->error = error; + promise->has_errored = EINA_TRUE; + + _ecore_promise_finish(promise); +} + +static void +_ecore_promise_finish(Ecore_Promise* promise) +{ + promise->has_finished = EINA_TRUE; + eina_condition_broadcast(&promise->condition); + _ecore_promise_unsafe_unref(promise); + if(!promise->is_then_calls_manual && !promise->has_pending_call) + { + promise->has_pending_call = EINA_TRUE; + _ecore_promise_lock_release(promise); + ecore_job_add((Ecore_Cb)&_ecore_promise_then_calls, promise); + } + else + _ecore_promise_lock_release(promise); +} + +static Eina_Bool +_ecore_promise_iterator_next(_Ecore_Promise_Success_Iterator *it, void **data) +{ + if(it->data.promise_index == it->data.num_promises) + return EINA_FALSE; + + if(ecore_promise_error_get(it->data.promises[it->data.promise_index])) + { + return EINA_FALSE; + } + else + { + *data = ecore_promise_value_get(it->data.promises[it->data.promise_index++]); + return EINA_TRUE; + } +} + +static void** +_ecore_promise_iterator_get_container(_Ecore_Promise_Success_Iterator *it) +{ + return (void**)it->data.promises; +} + +static void +_ecore_promise_iterator_free(_Ecore_Promise_Success_Iterator *it EINA_UNUSED) +{ +} + +static void _ecore_promise_iterator_setup(_Ecore_Promise_Iterator* it, Eina_Array* promises_array) +{ + Ecore_Promise** promises; + + it->success_iterator = &it->data.success_iterator_impl; + it->failure_iterator = &it->data.data.failure_iterator_impl; + it->data.data.num_promises = eina_array_count_get(promises_array); + it->data.data.promise_index = 0; + promises = (Ecore_Promise**)promises_array->data; + + memcpy(&it->data.data.promises[0], promises, it->data.data.num_promises*sizeof(Ecore_Promise*)); + + EINA_MAGIC_SET(&it->data.success_iterator_impl, EINA_MAGIC_ITERATOR); + EINA_MAGIC_SET(&it->data.data.failure_iterator_impl, EINA_MAGIC_ITERATOR); + + it->data.success_iterator_impl.version = EINA_ITERATOR_VERSION; + it->data.success_iterator_impl.next = FUNC_ITERATOR_NEXT(_ecore_promise_iterator_next); + it->data.success_iterator_impl.get_container = FUNC_ITERATOR_GET_CONTAINER( + _ecore_promise_iterator_get_container); + it->data.success_iterator_impl.free = FUNC_ITERATOR_FREE(_ecore_promise_iterator_free); +} + +EAPI int ecore_promise_value_size_get(Ecore_Promise const* promise) +{ + return promise->value_size; +} + +static void _ecore_promise_lock_take(Ecore_Promise* promise) +{ + eina_lock_take(&promise->lock); +} + +static void _ecore_promise_lock_release(Ecore_Promise* promise) +{ + eina_lock_release(&promise->lock); +} + +static void _ecore_promise_unsafe_ref(Ecore_Promise const* promise) +{ + Ecore_Promise* p = (Ecore_Promise*)promise; + ++p->ref; +} + +static void _ecore_promise_free_cb(Ecore_Promise* promise) +{ + _ecore_promise_lock_take(promise); + _ecore_promise_unlock_unsafe_free_unref(promise); +} + +static void _ecore_promise_unsafe_unref(Ecore_Promise const* promise) +{ + Ecore_Promise* p = (Ecore_Promise*)promise; + if(p->ref == 1 && !p->has_pending_call) + { + ecore_job_add((Ecore_Cb)_ecore_promise_free_cb, p); + } + else + --p->ref; +} + +static void _ecore_promise_unsafe_free_unref(Ecore_Promise const* promise) +{ + Ecore_Promise* p = (Ecore_Promise*)promise; + if(--p->ref == 0) + { + assert(!p->has_pending_call); + _ecore_promise_del(p); + } +} + +static Eina_Bool _ecore_promise_unlock_unsafe_free_unref(Ecore_Promise const* promise) +{ + Ecore_Promise* p = (Ecore_Promise*)promise; + if(--p->ref == 0) + { + assert(!p->has_pending_call); + _ecore_promise_lock_release((Ecore_Promise*)promise); + _ecore_promise_del(p); + return EINA_TRUE; + } + else + { + _ecore_promise_lock_release((Ecore_Promise*)promise); + return EINA_FALSE; + } +} + +EAPI void ecore_promise_ref(Ecore_Promise* promise) +{ + _ecore_promise_lock_take(promise); + _ecore_promise_unsafe_ref(promise); + _ecore_promise_lock_release(promise); +} + +EAPI void ecore_promise_unref(Ecore_Promise* promise) +{ + _ecore_promise_lock_take(promise); + _ecore_promise_unsafe_unref(promise); + _ecore_promise_lock_release(promise); +} diff --git a/src/lib/ecore/ecore_promise.h b/src/lib/ecore/ecore_promise.h new file mode 100644 index 0000000..834c336 --- /dev/null +++ b/src/lib/ecore/ecore_promise.h @@ -0,0 +1,136 @@ + +#ifdef EFL_BETA_API_SUPPORT + +struct _Ecore_Promise; + +/* + * @def _Ecore_Promise + */ +typedef struct _Ecore_Promise Ecore_Promise; + +/* + * @brief Function callback type for when using ecore_promise_then + */ +typedef void(*Ecore_Promise_Cb)(void* data, void* value); + +/* + * @brief Function callback type for when creating Ecore_Thread that + * uses Ecore_Promise for communication + */ +typedef void(*Ecore_Promise_Thread_Cb)(const void* data, Ecore_Promise* promise); + +/* + * @brief Function that instantiates a Ecore_Promise and automatically + * executes func_blocking callback function in another thread + */ +EAPI Ecore_Promise* ecore_promise_thread_run(Ecore_Promise_Thread_Cb func_blocking, const void* data, size_t value_size); + +/* + * @brief Creates a Ecore_Promise with a value of size value_size. + * + * @param value_size Size of value-type that Ecore_Promise will hold + */ +EAPI Ecore_Promise* ecore_promise_add(int value_size); + +/* + * @brief Appends a callback to be called when the Ecore_Promise is + * finished. + * + * @param promise The Ecore_Promise to wait for + * @param callback Callback to be called when Ecore_Promise is finished + * @param data Private data passed to the callback + */ +EAPI void ecore_promise_then(Ecore_Promise* promise, Ecore_Promise_Cb callback, void* data); + +/* + * @brief Creates a new Ecore_Promise from other Ecore_Promises + * + * @param promises An Eina_Iterator for all Ecore_Promises + */ +EAPI Ecore_Promise* ecore_promise_all(Eina_Iterator* promises); + +/* + * @brief Sets value for Ecore_Promise. This finishes the callback and + * calls all ecore_promise_then callbacks that have been registered on + * this Ecore_Promise. This function must be called only once per + * Ecore_Promise + * + * @param promise The promise for which to set the value + * @param value The pointer to the value that is going to be copied, or NULL. + */ +EAPI void ecore_promise_value_set(Ecore_Promise* promise, void* value); + +/* + * @brief Returns the pointer to the value if the Ecore_Promise is + * finished. Waits for it to be finished, otherwise. + * + * @param promise The promise for which to get the value + */ +EAPI void* ecore_promise_value_get(Ecore_Promise const* promise); + +/* + * @brief Returns the pointer to the buffer that holds the value. This + * function is useful to instantiate the value directly in the correct + * buffer, without needing to copy. The ecore_promise_value_set must + * still be called, possibly with NULL, to finish the Ecore_Promise + * and call the callbacks registered in it. + * + * @param promise The promise for which to get the buffer pointer + */ +EAPI void* ecore_promise_buffer_get(Ecore_Promise* promise); + +/* + * @brief Sets an error to the Ecore_Promise, thus finishing the + * promise and calling all ecore_promise_then callbacks registered. + * + * @param promise The promise for which to set the error + * @param error Eina_Error to be set + */ +EAPI void ecore_promise_error_set(Ecore_Promise* promise, Eina_Error error); + +/* + * @brief Gets an error to the Ecore_Promise if the promise is + * finished and has error'ed out. If it hasn't finished, it will wait, + * and if it has finished but otherwise not error'ed, returns 0. + * + * @param promise The promise for which to get the error + */ +EAPI Eina_Error ecore_promise_error_get(Ecore_Promise const* promise); + +/* + * @brief Gets the size of the value in ecore_promise_value_get. + * + * @param promise The promise for which to get the value size + */ +EAPI int ecore_promise_value_size_get(Ecore_Promise const* promise); + +/* + * @brief Returns @EINA_TRUE if the promise is ready and won't block + * on ecore_promise_value_get and @EINA_FALSE otherwise. + * + * @param promise The promise for which to get the ready status + */ +EAPI Eina_Bool ecore_promise_ready_is(Ecore_Promise const* promise); + +/* + * @brief Increments the reference count for the Ecore_Promise + * + * @param promise The promise for which to increment its reference + */ +EAPI void ecore_promise_ref(Ecore_Promise* promise); + +/* + * @brief Decrement the reference count for the Ecore_Promise and + * possibly schedule its destruction. The Ecore_Promise, if its + * reference count drops to zero, will only be free'd when all the + * current mainloop events have been processed. This allows the user + * to call ecore_promise_then before that happens so it can increment + * the reference back to 1 and wait for a value set or error set on + * the Ecore_Promise. + * + * @param promise The promise for which to decrement its reference + */ +EAPI void ecore_promise_unref(Ecore_Promise* promise); + +#endif + diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h index ab920ee..936c8c8 100644 --- a/src/lib/eo/Eo.h +++ b/src/lib/eo/Eo.h @@ -511,61 +511,96 @@ typedef struct _Eo_Call_Cache __FILE__, __LINE__)) return DefRet; \ _Eo_##Name##_func _func_ = (_Eo_##Name##_func) ___call.func; \ +#define _EO_FUNC_PROMISE_CREATE0 +#define _EO_FUNC_PROMISE_FREE0 +#define _EO_FUNC_PROMISE_CREATE1 \ + Ecore_Promise*(*ecore_promise_add)(int size) = dlsym(dlopen(NULL, RTLD_NOW), "ecore_promise_add"); \ + Ecore_Promise* __eo_promise = ecore_promise_add(sizeof(PromiseValue)); +#define _EO_FUNC_PROMISE_FREE1 \ + if(Promise) \ + *Promise = __eo_promise; \ + else \ + { \ + void(*ecore_promise_unref)(Ecore_Promise* p) = dlsym(dlopen(NULL, RTLD_NOW), "ecore_promise_unref"); \ + ecore_promise_unref(__eo_promise); \ + } +#define _EO_EXPANSION_AUX(X) X +#define _EO_FUNC_PROMISE_CREATE(p) _EO_EXPANSION_AUX(_EO_FUNC_PROMISE_CREATE ## p) +#define _EO_FUNC_PROMISE_FREE(p) _EO_EXPANSION_AUX(_EO_FUNC_PROMISE_FREE ## p) + // to define an EAPI function -#define _EO_FUNC_BODY(Name, ObjType, Ret, DefRet) \ +#define _EO_FUNC_BODY(Name, ObjType, Promise, Ret, DefRet) \ Ret \ Name(ObjType obj) \ { \ typedef Ret (*_Eo_##Name##_func)(Eo *, void *obj_data); \ Ret _r; \ EO_FUNC_COMMON_OP(obj, Name, DefRet); \ + _EO_FUNC_PROMISE_CREATE(Promise) \ _r = _func_(___call.eo_id, ___call.data); \ _eo_call_end(&___call); \ + _EO_FUNC_PROMISE_FREE(Promise) \ return _r; \ } -#define _EO_VOID_FUNC_BODY(Name, ObjType) \ +#define _EO_VOID_FUNC_BODY(Name, ObjType, Promise) \ void \ Name(ObjType obj) \ { \ typedef void (*_Eo_##Name##_func)(Eo *, void *obj_data); \ EO_FUNC_COMMON_OP(obj, Name, ); \ + _EO_FUNC_PROMISE_CREATE(Promise) \ _func_(___call.eo_id, ___call.data); \ _eo_call_end(&___call); \ + _EO_FUNC_PROMISE_FREE(Promise) \ } -#define _EO_FUNC_BODYV(Name, ObjType, Ret, DefRet, Arguments, ...) \ +#define _EO_FUNC_BODYV(Name, ObjType, Promise, Ret, DefRet, Arguments, ...) \ Ret \ Name(ObjType obj, __VA_ARGS__) \ { \ typedef Ret (*_Eo_##Name##_func)(Eo *, void *obj_data, __VA_ARGS__); \ Ret _r; \ EO_FUNC_COMMON_OP(obj, Name, DefRet); \ + _EO_FUNC_PROMISE_CREATE(Promise) \ _r = _func_(___call.eo_id, ___call.data, Arguments); \ _eo_call_end(&___call); \ + _EO_FUNC_PROMISE_FREE(Promise) \ return _r; \ } -#define _EO_VOID_FUNC_BODYV(Name, ObjType, Arguments, ...) \ +#define _EO_VOID_FUNC_BODYV(Name, ObjType, Promise, Arguments, ...) \ void \ Name(ObjType obj, __VA_ARGS__) \ { \ typedef void (*_Eo_##Name##_func)(Eo *, void *obj_data, __VA_ARGS__); \ EO_FUNC_COMMON_OP(obj, Name, ); \ + _EO_FUNC_PROMISE_CREATE(Promise) \ _func_(___call.eo_id, ___call.data, Arguments); \ _eo_call_end(&___call); \ + _EO_FUNC_PROMISE_FREE(Promise) \ } -#define EO_FUNC_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, Ret, DefRet) -#define EO_VOID_FUNC_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *) -#define EO_FUNC_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) -#define EO_VOID_FUNC_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, EO_FUNC_CALL(Arguments), __VA_ARGS__) - -#define EO_FUNC_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, Ret, DefRet) -#define EO_VOID_FUNC_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *) -#define EO_FUNC_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) -#define EO_VOID_FUNC_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, EO_FUNC_CALL(Arguments), __VA_ARGS__) - +#define EO_FUNC_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, 0, Ret, DefRet) +#define EO_VOID_FUNC_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *, 0) +#define EO_FUNC_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, 0, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) +#define EO_VOID_FUNC_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, 0, EO_FUNC_CALL(Arguments), __VA_ARGS__) + +#define EO_FUNC_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, 0, Ret, DefRet) +#define EO_VOID_FUNC_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *, 0) +#define EO_FUNC_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, 0, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) +#define EO_VOID_FUNC_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, 0, EO_FUNC_CALL(Arguments), __VA_ARGS__) + +#define EO_FUNC_PROMISE_BODY(Name, Ret, DefRet) _EO_FUNC_BODY(Name, Eo *, 1, Ret, DefRet) +#define EO_VOID_FUNC_PROMISE_BODY(Name) _EO_VOID_FUNC_BODY(Name, Eo *, 1) +#define EO_FUNC_PROMISE_BODYV(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, Eo *, 1, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) +#define EO_VOID_FUNC_PROMISE_BODYV(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, Eo *, 1, EO_FUNC_CALL(Arguments), __VA_ARGS__) + +#define EO_FUNC_PROMISE_BODY_CONST(Name, Ret, DefRet) _EO_FUNC_BODY(Name, const Eo *, 1, Ret, DefRet) +#define EO_VOID_FUNC_PROMISE_BODY_CONST(Name) _EO_VOID_FUNC_BODY(Name, const Eo *, 1) +#define EO_FUNC_PROMISE_BODYV_CONST(Name, Ret, DefRet, Arguments, ...) _EO_FUNC_BODYV(Name, const Eo *, 1, Ret, DefRet, EO_FUNC_CALL(Arguments), __VA_ARGS__) +#define EO_VOID_FUNC_PROMISE_BODYV_CONST(Name, Arguments, ...) _EO_VOID_FUNC_BODYV(Name, const Eo *, 1, EO_FUNC_CALL(Arguments), __VA_ARGS__) + #ifndef _WIN32 # define _EO_OP_API_ENTRY(a) (void*)a #else diff --git a/src/lib/eolian/eo_lexer.c b/src/lib/eolian/eo_lexer.c index 0e98313..1aeb6a9 100644 --- a/src/lib/eolian/eo_lexer.c +++ b/src/lib/eolian/eo_lexer.c @@ -75,7 +75,8 @@ static const char * const ctypes[] = "Eina_Accessor", "Eina_Array", "Eina_Iterator", "Eina_Hash", "Eina_List", "Eina_Value", - "Eo_Event_Cb" + "Eo_Event_Cb", + "Ecore_Promise" }; #undef KW diff --git a/src/lib/eolian/eo_lexer.h b/src/lib/eolian/eo_lexer.h index b28a3fc..bb4385b 100644 --- a/src/lib/eolian/eo_lexer.h +++ b/src/lib/eolian/eo_lexer.h @@ -52,7 +52,9 @@ enum Tokens \ KW(accessor), KW(array), KW(iterator), KW(hash), KW(list), KW(generic_value), \ \ - KW(__builtin_event_cb), KW(__undefined_type), \ + KW(__builtin_event_cb), \ + KW(Promise), \ + KW(__undefined_type), \ \ KW(true), KW(false), KW(null) @@ -206,4 +208,4 @@ void eo_lexer_context_pop (Eo_Lexer *ls); void eo_lexer_context_restore(Eo_Lexer *ls); void eo_lexer_context_clear (Eo_Lexer *ls); -#endif /* __EO_LEXER_H__ */ \ No newline at end of file +#endif /* __EO_LEXER_H__ */ diff --git a/src/lib/eolian/eo_parser.c b/src/lib/eolian/eo_parser.c index 6b6df14..a0d9ddd 100644 --- a/src/lib/eolian/eo_parser.c +++ b/src/lib/eolian/eo_parser.c @@ -782,7 +782,7 @@ parse_type_void_base(Eo_Lexer *ls, Eina_Bool noptr) _fill_name(eina_stringshare_ref(ls->t.value.s), &def->full_name, &def->name, &def->namespaces); eo_lexer_get(ls); - if (tpid >= KW_accessor && tpid <= KW_list) + if ((tpid >= KW_accessor && tpid <= KW_list) || tpid == KW_Promise) { int bline = ls->line_number, bcol = ls->column; def->type = EOLIAN_TYPE_COMPLEX; diff --git a/src/tests/ecore/ecore_suite.c b/src/tests/ecore/ecore_suite.c index 787a455..04ad191 100644 --- a/src/tests/ecore/ecore_suite.c +++ b/src/tests/ecore/ecore_suite.c @@ -26,6 +26,7 @@ static const Efl_Test_Case etc[] = { #endif { "Ecore_Input", ecore_test_ecore_input }, { "Ecore_File", ecore_test_ecore_file }, + { "Ecore_Promise", ecore_test_ecore_promise }, { NULL, NULL } }; diff --git a/src/tests/ecore/ecore_suite.h b/src/tests/ecore/ecore_suite.h index f0e4c2a..558e610 100644 --- a/src/tests/ecore/ecore_suite.h +++ b/src/tests/ecore/ecore_suite.h @@ -15,5 +15,6 @@ void ecore_test_ecore_drm(TCase *tc); void ecore_test_ecore_fb(TCase *tc); void ecore_test_ecore_input(TCase *tc); void ecore_test_ecore_file(TCase *tc); +void ecore_test_ecore_promise(TCase *tc); #endif /* _ECORE_SUITE_H */ diff --git a/src/tests/ecore/ecore_test_promise.c b/src/tests/ecore/ecore_test_promise.c new file mode 100644 index 0000000..0f003fd --- /dev/null +++ b/src/tests/ecore/ecore_test_promise.c @@ -0,0 +1,364 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <Ecore.h> +#include "ecore_suite.h" +#include <time.h> + +void promised_thread(const void* data EINA_UNUSED, Ecore_Promise* promise) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_promise_value_set(promise, NULL); +} + +void promise_callback(void* data EINA_UNUSED, void* value EINA_UNUSED) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_main_loop_quit(); +} + +START_TEST(ecore_test_promise) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + Ecore_Promise* promise = ecore_promise_thread_run(&promised_thread, NULL, 0); + ecore_promise_then(promise, &promise_callback, NULL); + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +void promise_error_thread(const void* data EINA_UNUSED, Ecore_Promise* promise) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_promise_error_set(promise, EINA_ERROR_OUT_OF_MEMORY); +} + +void promise_error_callback(void* data EINA_UNUSED, void* value EINA_UNUSED) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_main_loop_quit(); +} + +START_TEST(ecore_test_promise_error) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + Ecore_Promise* promise = ecore_promise_thread_run(&promise_error_thread, NULL, 0); + ecore_promise_then(promise, &promise_error_callback, NULL); + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +START_TEST(ecore_test_promise_all) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + Ecore_Promise* first[2] = { ecore_promise_thread_run(&promised_thread, NULL, 0), NULL }; + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + ecore_promise_then(promise, &promise_callback, NULL); + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +void promise_callback2(void* data, void* value EINA_UNUSED) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + if(++(*(int*)data) == 2) + ecore_main_loop_quit(); +} + +START_TEST(ecore_test_promise_all_then_then) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + int i = 0; + + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + Ecore_Promise* first[2] = { ecore_promise_thread_run(&promised_thread, NULL, 0), NULL }; + ecore_promise_then(first[0], &promise_callback2, &i); + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + ecore_promise_then(promise, &promise_callback2, &i); + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +struct sync_data +{ + Eina_Lock lock; + Eina_Condition cond; + Eina_Bool var; +}; + +void promised_exit_thread(struct sync_data* data EINA_UNUSED, Ecore_Promise* promise) +{ + fprintf(stderr, "%s:%d %s\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_promise_value_set(promise, NULL); + eina_lock_take(&data->lock); + data->var = EINA_TRUE; + eina_condition_broadcast(&data->cond); + eina_lock_release(&data->lock); +} + +static void _ecore_test_promise_then_after_thread_finished_main_cb() +{ + struct sync_data data; + data.var = EINA_FALSE; + eina_lock_new(&data.lock); + eina_condition_new(&data.cond, &data.lock); + + Ecore_Promise* promise = ecore_promise_thread_run((Ecore_Promise_Thread_Cb)&promised_exit_thread, &data, 0); + + eina_lock_take(&data.lock); + while(!data.var) + { + eina_condition_wait(&data.cond); + } + eina_lock_release(&data.lock); + ecore_promise_then(promise, &promise_callback, NULL); +} + +START_TEST(ecore_test_promise_then_after_thread_finished) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_job_add(&_ecore_test_promise_then_after_thread_finished_main_cb, NULL); + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +static void _ecore_test_promise_then_after_thread_finished_all_main_cb() +{ + struct sync_data data; + data.var = EINA_FALSE; + eina_lock_new(&data.lock); + eina_condition_new(&data.cond, &data.lock); + + Ecore_Promise* first[] = {ecore_promise_thread_run((Ecore_Promise_Thread_Cb)&promised_exit_thread, &data, 0), NULL}; + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + + eina_lock_take(&data.lock); + while(!data.var) + { + eina_condition_wait(&data.cond); + } + eina_lock_release(&data.lock); + ecore_promise_then(promise, &promise_callback, NULL); +} + +START_TEST(ecore_test_promise_then_after_thread_finished_all) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_job_add(&_ecore_test_promise_then_after_thread_finished_all_main_cb, NULL); + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +void promised_block_thread(const void* data EINA_UNUSED, Ecore_Promise* promise) +{ + struct timespec v = {.tv_sec = 1, .tv_nsec = 0}, rem; + if(nanosleep(&v, &rem) == -1 && errno == EINTR) + do + { + v = rem; + } + while(nanosleep(&v, &rem) == -1 && errno == EINTR); + + int r = 10; + ecore_promise_value_set(promise, &r); +} + +static void +_ecore_test_promise_blocking_get_quit_cb(void* data EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +static void +_ecore_test_promise_blocking_get_main_cb(void* data EINA_UNUSED) +{ + Ecore_Promise* promise = ecore_promise_thread_run(&promised_block_thread, NULL, sizeof(int)); + const void* value = ecore_promise_value_get(promise); + ck_assert(*(int*)value == 10); + + ecore_job_add(&_ecore_test_promise_blocking_get_quit_cb, NULL); +} + +START_TEST(ecore_test_promise_blocking_get) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_job_add(&_ecore_test_promise_blocking_get_main_cb, NULL); + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +static void +_ecore_test_promise_blocking_get_all_value_get_cb(Ecore_Promise* promise, Ecore_Thread* thread EINA_UNUSED) +{ + Eina_Iterator** iterator = ecore_promise_value_get(promise); + int* v; + ck_assert(eina_iterator_next(*iterator, (void**)&v)); + ck_assert(*v == 10); + ecore_main_loop_quit(); +} + +static void +_ecore_test_promise_blocking_get_all_main_cb(void* data EINA_UNUSED) +{ + Ecore_Promise* first[2] = {ecore_promise_thread_run(&promised_block_thread, NULL, sizeof(int)), NULL}; + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + + ecore_thread_run((Ecore_Thread_Cb)&_ecore_test_promise_blocking_get_all_value_get_cb, NULL, NULL, promise); +} + +START_TEST(ecore_test_promise_blocking_get_all) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + ecore_job_add(&_ecore_test_promise_blocking_get_all_main_cb, NULL); + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +static void +_ecore_test_promise_normal_lifetime_cb(void* data EINA_UNUSED, void* value EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +START_TEST(ecore_test_promise_normal_lifetime) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + Ecore_Promise* promise = ecore_promise_add(0); + + ecore_promise_then(promise, &_ecore_test_promise_normal_lifetime_cb, NULL); + ecore_promise_value_set(promise, NULL); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +START_TEST(ecore_test_promise_normal_lifetime_all) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + Ecore_Promise* first[2] = {ecore_promise_add(0), NULL}; + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + + ecore_promise_then(promise, &_ecore_test_promise_normal_lifetime_cb, NULL); + ecore_promise_value_set(promise, NULL); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +static void +_ecore_test_promise_immediate_set_lifetime_cb(void* data EINA_UNUSED, void* value EINA_UNUSED) +{ + ecore_main_loop_quit(); +} + +START_TEST(ecore_test_promise_immediate_set_lifetime) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + Ecore_Promise* promise = ecore_promise_add(0); + + ecore_promise_value_set(promise, NULL); + ecore_promise_then(promise, &_ecore_test_promise_immediate_set_lifetime_cb, NULL); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +START_TEST(ecore_test_promise_immediate_set_lifetime_all) +{ + ecore_init(); + fprintf(stderr, "%s:%d %s ---------- BEGIN test\n", __FILE__, __LINE__, __func__); fflush(stderr); + + Ecore_Promise* first[2] = {ecore_promise_add(0), NULL}; + Ecore_Promise* promise = ecore_promise_all(eina_carray_iterator_new((void**)&first[0])); + + ecore_promise_value_set(first[0], NULL); + ecore_promise_then(promise, &_ecore_test_promise_immediate_set_lifetime_cb, NULL); + + ecore_main_loop_begin(); + + fprintf(stderr, "%s:%d %s ---------- END test\n", __FILE__, __LINE__, __func__); fflush(stderr); + ecore_shutdown(); +} +END_TEST + +void ecore_test_ecore_promise(TCase *tc EINA_UNUSED) +{ + tcase_add_test(tc, ecore_test_promise); + tcase_add_test(tc, ecore_test_promise_error); + tcase_add_test(tc, ecore_test_promise_all); + tcase_add_test(tc, ecore_test_promise_all_then_then); + tcase_add_test(tc, ecore_test_promise_then_after_thread_finished); + tcase_add_test(tc, ecore_test_promise_then_after_thread_finished_all); + tcase_add_test(tc, ecore_test_promise_blocking_get); + tcase_add_test(tc, ecore_test_promise_blocking_get_all); + tcase_add_test(tc, ecore_test_promise_normal_lifetime); + tcase_add_test(tc, ecore_test_promise_normal_lifetime_all); + tcase_add_test(tc, ecore_test_promise_immediate_set_lifetime); + tcase_add_test(tc, ecore_test_promise_immediate_set_lifetime_all); +} --