From e5ddfb4b2b76304aae9c7c15f465d7ef9863a2c0 Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Wed, 7 Jan 2015 16:42:24 +0100 Subject: [PATCH] ecore: add ecore_thread_wait and necessary infrastructure. This enable the possibility to block the main loop until a specific thread is done. It may trigger still process ending of other thread during that function call, but not any other type of event (timer, animator, idler, ... are all ignored). --- src/lib/ecore/Ecore_Common.h | 22 +++++++++++ src/lib/ecore/ecore.c | 6 +++ src/lib/ecore/ecore_thread.c | 73 ++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/src/lib/ecore/Ecore_Common.h b/src/lib/ecore/Ecore_Common.h index c218f41ed6..7a8c306d80 100644 --- a/src/lib/ecore/Ecore_Common.h +++ b/src/lib/ecore/Ecore_Common.h @@ -385,6 +385,19 @@ EAPI void ecore_main_loop_thread_safe_call_async(Ecore_Cb callback, void *data); */ EAPI void *ecore_main_loop_thread_safe_call_sync(Ecore_Data_Cb callback, void *data); +/** + * @brief Wait for the next thread call in the main loop. + * @since 1.13.0 + * + * @param wait How long to wait for this callback to be called + * + * Note: This function should only be called in the main loop + * and will actually block the main loop until either a call + * is triggered from a thread or the time specified by wait has + * passed. + */ +EAPI void ecore_main_loop_thread_safe_call_wait(double wait); + /** * @brief This function suspend the main loop in a know state * @since 1.1.0 @@ -1818,6 +1831,15 @@ EAPI Ecore_Thread *ecore_thread_feedback_run(Ecore_Thread_Cb func_heavy, Ecore_T */ EAPI Eina_Bool ecore_thread_cancel(Ecore_Thread *thread); +/** + * @brief Block the main loop until the thread execution is over. + * + * @param thread The thread to wait on. + * @param wait Maximum time to wait before exiting anyway. + * @return EINA_TRUE if the thread execution is over. + */ +EAPI Eina_Bool ecore_thread_wait(Ecore_Thread *thread, double wait); + /** * Checks if a thread is pending cancellation * diff --git a/src/lib/ecore/ecore.c b/src/lib/ecore/ecore.c index d0ee1e95f5..0e485843fc 100644 --- a/src/lib/ecore/ecore.c +++ b/src/lib/ecore/ecore.c @@ -583,6 +583,12 @@ ecore_main_loop_thread_safe_call_sync(Ecore_Data_Cb callback, return ret; } +EAPI void +ecore_main_loop_thread_safe_call_wait(double wait) +{ + ecore_pipe_wait(_thread_call, 1, wait); +} + EAPI int ecore_thread_main_loop_begin(void) { diff --git a/src/lib/ecore/ecore_thread.c b/src/lib/ecore/ecore_thread.c index a3202f6585..2be9db0edc 100644 --- a/src/lib/ecore/ecore_thread.c +++ b/src/lib/ecore/ecore_thread.c @@ -733,6 +733,79 @@ ecore_thread_cancel(Ecore_Thread *thread) return EINA_FALSE; } +typedef struct _Ecore_Thread_Waiter Ecore_Thread_Waiter; +struct _Ecore_Thread_Waiter +{ + Ecore_Thread_Cb func_cancel; + Ecore_Thread_Cb func_end; + const void *data; +}; + +static void +_ecore_thread_wait_reset(Ecore_Thread_Waiter *waiter, + Ecore_Pthread_Worker *worker) +{ + worker->data = waiter->data; + worker->func_cancel = waiter->func_cancel; + worker->func_end = waiter->func_end; + // The waiter will be checked by _wait, NULL meaning it is done + waiter->func_end = NULL; + waiter->func_cancel = NULL; + waiter->data = NULL; +} + +static void +_ecore_thread_wait_cancel(void *data, Ecore_Thread *thread) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker*) thread; + Ecore_Thread_Waiter *waiter = data; + + waiter->func_cancel((void*) waiter->data, thread); + _ecore_thread_wait_reset(waiter, worker); +} + +static void +_ecore_thread_wait_end(void *data, Ecore_Thread *thread) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker*) thread; + Ecore_Thread_Waiter *waiter = data; + + waiter->func_end((void*) waiter->data, thread); + _ecore_thread_wait_reset(waiter, worker); +} + +EAPI Eina_Bool +ecore_thread_wait(Ecore_Thread *thread, double wait) +{ + Ecore_Pthread_Worker *worker = (Ecore_Pthread_Worker*) thread; + Ecore_Thread_Waiter waiter; + + if (!thread) return ; + + waiter.data = worker->data; + waiter.func_end = worker->func_end; + waiter.func_cancel = worker->func_cancel; + // Now trick the thread to call the wrapper function + worker->data = &waiter; + worker->func_cancel = _ecore_thread_wait_cancel; + worker->func_end = _ecore_thread_wait_end; + + while (waiter.data) + { + double start, end; + + start = ecore_time_get(); + ecore_main_loop_thread_safe_call_wait(wait); + end = ecore_time_get(); + + wait -= end - start; + + if (wait <= 0) break; + } + + return (waiter.data == NULL) ? EINA_TRUE : EINA_FALSE; +} + EAPI Eina_Bool ecore_thread_check(Ecore_Thread *thread) {