diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am index e5d5efc981..084256eea3 100644 --- a/src/Makefile_Ecore.am +++ b/src/Makefile_Ecore.am @@ -66,6 +66,7 @@ lib/ecore/ecore_poller.c \ lib/ecore/ecore_time.c \ lib/ecore/ecore_timer.c \ lib/ecore/ecore_thread.c \ +lib/ecore/ecore_thread_promise.c \ lib/ecore/ecore_throttle.c \ lib/ecore/ecore_exe.c \ lib/ecore/ecore_exe_private.h \ diff --git a/src/lib/ecore/Ecore_Common.h b/src/lib/ecore/Ecore_Common.h index fe2c5c9012..711ea7a07c 100644 --- a/src/lib/ecore/Ecore_Common.h +++ b/src/lib/ecore/Ecore_Common.h @@ -3089,6 +3089,26 @@ typedef Eo Ecore_Job; /**< A job handle */ * @} */ + +#ifdef EFL_BETA_API_SUPPORT + +/* + * @brief Function callback type for when creating Ecore_Thread that + * uses Ecore_Promise for communication + */ +typedef void(*Ecore_Thread_Promise_Cb)(const void* data, Eina_Promise_Owner* promise, Ecore_Thread* thread); + +/* + * @brief Function that instantiates a Ecore_Promise and automatically + * executes func_blocking callback function in another thread + */ +EAPI Ecore_Thread* ecore_thread_promise_run(Ecore_Thread_Promise_Cb func_heavy, + Ecore_Thread_Promise_Cb func_cancel, + const void* data, size_t value_size, + Eina_Promise** promise); + +#endif + #ifdef __cplusplus } #endif diff --git a/src/lib/ecore/ecore_thread_promise.c b/src/lib/ecore/ecore_thread_promise.c new file mode 100644 index 0000000000..dbc3ce9bbd --- /dev/null +++ b/src/lib/ecore/ecore_thread_promise.c @@ -0,0 +1,137 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +struct _Ecore_Thread_Data +{ + Ecore_Thread_Promise_Cb func_blocking; + Ecore_Thread_Promise_Cb func_cancel; + void const* data; + Ecore_Thread* thread; +}; +typedef struct _Ecore_Thread_Data _Ecore_Thread_Data; + +struct _Ecore_Thread_Promise_Owner +{ + Eina_Promise_Owner owner_vtable; + Eina_Promise_Owner* eina_owner; + _Ecore_Thread_Data thread_callback_data; +}; +typedef struct _Ecore_Thread_Promise_Owner _Ecore_Thread_Promise_Owner; + +static void _ecore_promise_thread_end(void* data, Ecore_Thread* thread EINA_UNUSED) +{ + _Ecore_Thread_Promise_Owner* p = data; + if(!eina_promise_owner_pending_is(p->eina_owner)) + { + eina_promise_owner_default_manual_then_set(p->eina_owner, EINA_FALSE); + eina_promise_owner_default_call_then(p->eina_owner); + } + else + { + eina_promise_owner_default_manual_then_set(p->eina_owner, EINA_FALSE); + } +} + +static void +_ecore_promise_thread_blocking(void* data, Ecore_Thread* thread EINA_UNUSED) +{ + _Ecore_Thread_Promise_Owner* promise = data; + (promise->thread_callback_data.func_blocking)(promise->thread_callback_data.data, &promise->owner_vtable, thread); +} + +static void _ecore_promise_thread_notify(void* data, Ecore_Thread* thread EINA_UNUSED, void* msg_data) +{ + _Ecore_Thread_Promise_Owner* promise = data; + eina_promise_owner_progress(promise->eina_owner, msg_data); +} + +static void _ecore_promise_cancel(void* data, Eina_Promise_Owner* promise EINA_UNUSED) +{ + _Ecore_Thread_Promise_Owner* priv = data; + (priv->thread_callback_data.func_cancel)(priv->thread_callback_data.data, &priv->owner_vtable, + priv->thread_callback_data.thread); +} + +static void _ecore_promise_thread_cancel(void* data, Ecore_Thread* thread EINA_UNUSED) +{ + _Ecore_Thread_Promise_Owner* owner = data; + Eina_Promise* promise; + + promise = eina_promise_owner_promise_get(owner->eina_owner); + eina_promise_cancel(promise); +} + +static void* _ecore_promise_owner_buffer_get(_Ecore_Thread_Promise_Owner* promise) +{ + return promise->eina_owner->buffer_get(promise->eina_owner); +} +static size_t _ecore_promise_owner_value_size_get(_Ecore_Thread_Promise_Owner const* promise) +{ + return promise->eina_owner->value_size_get(promise->eina_owner); +} +static void _ecore_promise_owner_value_set(_Ecore_Thread_Promise_Owner* promise, void* data, Eina_Promise_Free_Cb free) +{ + promise->eina_owner->value_set(promise->eina_owner, data, free); +} +static void _ecore_promise_owner_error_set(_Ecore_Thread_Promise_Owner* promise, Eina_Error error) +{ + promise->eina_owner->error_set(promise->eina_owner, error); +} +static Eina_Bool _ecore_promise_owner_pending_is(_Ecore_Thread_Promise_Owner const* promise) +{ + return promise->eina_owner->pending_is(promise->eina_owner); +} +static Eina_Bool _ecore_promise_owner_cancelled_is(_Ecore_Thread_Promise_Owner const* promise) +{ + return promise->eina_owner->cancelled_is(promise->eina_owner); +} +static Eina_Promise* _ecore_thread_promise_owner_promise_get(_Ecore_Thread_Promise_Owner* promise) +{ + return promise->eina_owner->promise_get(promise->eina_owner); +} +static void _ecore_thread_promise_owner_progress(_Ecore_Thread_Promise_Owner* promise, void* data) +{ + ecore_thread_feedback(promise->thread_callback_data.thread, data); +} + +Ecore_Thread* ecore_thread_promise_run(Ecore_Thread_Promise_Cb func_blocking, + Ecore_Thread_Promise_Cb func_cancel, + const void* data, size_t value_size, + Eina_Promise** promise) +{ + _Ecore_Thread_Promise_Owner* priv; + + priv = malloc(sizeof(_Ecore_Thread_Promise_Owner)); + + priv->eina_owner = eina_promise_default_add(value_size); + + priv->owner_vtable.version = EINA_PROMISE_VERSION; + priv->owner_vtable.value_set = EINA_FUNC_PROMISE_OWNER_VALUE_SET(&_ecore_promise_owner_value_set); + priv->owner_vtable.error_set = EINA_FUNC_PROMISE_OWNER_ERROR_SET(&_ecore_promise_owner_error_set); + priv->owner_vtable.buffer_get = EINA_FUNC_PROMISE_OWNER_BUFFER_GET(&_ecore_promise_owner_buffer_get); + priv->owner_vtable.value_size_get = EINA_FUNC_PROMISE_OWNER_VALUE_SIZE_GET(&_ecore_promise_owner_value_size_get); + priv->owner_vtable.promise_get = EINA_FUNC_PROMISE_OWNER_PROMISE_GET(&_ecore_thread_promise_owner_promise_get); + priv->owner_vtable.pending_is = EINA_FUNC_PROMISE_OWNER_PENDING_IS(&_ecore_promise_owner_pending_is); + priv->owner_vtable.cancelled_is = EINA_FUNC_PROMISE_OWNER_CANCELLED_IS(&_ecore_promise_owner_cancelled_is); + priv->owner_vtable.progress = EINA_FUNC_PROMISE_OWNER_PROGRESS(&_ecore_thread_promise_owner_progress); + + priv->thread_callback_data.data = data; + priv->thread_callback_data.func_blocking = func_blocking; + priv->thread_callback_data.func_cancel = func_cancel; + eina_promise_owner_default_manual_then_set(priv->eina_owner, EINA_TRUE); + if(func_cancel) + eina_promise_owner_default_cancel_cb_add(priv->eina_owner, &_ecore_promise_cancel, priv, NULL); + priv->thread_callback_data.thread = + ecore_thread_feedback_run(&_ecore_promise_thread_blocking, &_ecore_promise_thread_notify, + &_ecore_promise_thread_end, &_ecore_promise_thread_cancel, priv, + EINA_FALSE); + if(promise) + *promise = priv->eina_owner->promise_get(priv->eina_owner); + return priv->thread_callback_data.thread; +}