From 2bb8e5ad8e0fb75df462b8919152ad3bf06cd9ac Mon Sep 17 00:00:00 2001 From: Cedric BAIL Date: Fri, 31 Jul 2009 17:06:11 +0000 Subject: [PATCH] * ecore_thread_run: Add a facility to run heavy code in another thread that still integrate cleanly with the EFL. ecore_thread_run need two callbacks : * func_heavy is called from another thread and should not use the EFL except Eina, but carefully. * func_end is called when func_heavy is done, but from inside ecore main loop, so you can at this point call every EFL functions without fear. Note : The system automatically detect how many CPU you have and will spread the load on all of them. You must not assume that the result will come in the same order you requested it. Depend on each CPU load and how heavy the function on it are. SVN revision: 41555 --- legacy/ecore/configure.ac | 6 + legacy/ecore/src/lib/ecore/Ecore.h | 4 + legacy/ecore/src/lib/ecore/Makefile.am | 3 +- legacy/ecore/src/lib/ecore/ecore.c | 2 + legacy/ecore/src/lib/ecore/ecore_private.h | 3 +- legacy/ecore/src/lib/ecore/ecore_thread.c | 221 +++++++++++++++++++++ 6 files changed, 237 insertions(+), 2 deletions(-) create mode 100644 legacy/ecore/src/lib/ecore/ecore_thread.c diff --git a/legacy/ecore/configure.ac b/legacy/ecore/configure.ac index 989212c3f2..1a1bb4e4ef 100644 --- a/legacy/ecore/configure.ac +++ b/legacy/ecore/configure.ac @@ -197,6 +197,12 @@ requirements_ecore_x="" requirements_ecore_win32="" requirements_ecore_wince="" +# basic pthread support +AC_CHECK_HEADER(pthread.h, + [ + AC_DEFINE(BUILD_PTHREAD, 1, [Build thread worker]) + ] +) ### Additional options to configure diff --git a/legacy/ecore/src/lib/ecore/Ecore.h b/legacy/ecore/src/lib/ecore/Ecore.h index f6c8a5f564..db95288c3f 100644 --- a/legacy/ecore/src/lib/ecore/Ecore.h +++ b/legacy/ecore/src/lib/ecore/Ecore.h @@ -59,6 +59,8 @@ # include #endif +#include + #ifndef TRUE # define TRUE 1 #endif @@ -300,6 +302,8 @@ extern "C" { EAPI void ecore_pipe_write_close(Ecore_Pipe *p); EAPI void ecore_pipe_read_close(Ecore_Pipe *p); + EAPI Eina_Bool ecore_thread_run(void (*func_heavy)(void *data), void (*func_end)(void *data), const void *data); + EAPI double ecore_time_get(void); EAPI double ecore_loop_time_get(void); diff --git a/legacy/ecore/src/lib/ecore/Makefile.am b/legacy/ecore/src/lib/ecore/Makefile.am index e7e4219d5a..5c2dcf65b4 100644 --- a/legacy/ecore/src/lib/ecore/Makefile.am +++ b/legacy/ecore/src/lib/ecore/Makefile.am @@ -34,7 +34,8 @@ ecore_strbuf.c \ ecore_time.c \ ecore_timer.c \ ecore_tree.c \ -ecore_value.c +ecore_value.c \ +ecore_thread.c libecore_la_LIBADD = @dlopen_libs@ @EINA_LIBS@ @EVIL_LIBS@ @WIN32_LIBS@ -lm libecore_la_LDFLAGS = -no-undefined @lt_enable_auto_import@ -version-info @version_info@ @ecore_release_info@ diff --git a/legacy/ecore/src/lib/ecore/ecore.c b/legacy/ecore/src/lib/ecore/ecore.c index 2b5c476c34..59dcafcd4d 100644 --- a/legacy/ecore/src/lib/ecore/ecore.c +++ b/legacy/ecore/src/lib/ecore/ecore.c @@ -89,6 +89,7 @@ ecore_init(void) if (_ecore_fps_debug) _ecore_fps_debug_init(); _ecore_signal_init(); _ecore_exe_init(); + ecore_thread_init(); _ecore_loop_time = ecore_time_get(); } @@ -114,6 +115,7 @@ ecore_shutdown(void) if (_ecore_fps_debug) _ecore_fps_debug_shutdown(); _ecore_poller_shutdown(); _ecore_animator_shutdown(); + ecore_thread_shutdown(); _ecore_exe_shutdown(); _ecore_idle_enterer_shutdown(); _ecore_idle_exiter_shutdown(); diff --git a/legacy/ecore/src/lib/ecore/ecore_private.h b/legacy/ecore/src/lib/ecore/ecore_private.h index 5a7eee1c37..1badc6743e 100644 --- a/legacy/ecore/src/lib/ecore/ecore_private.h +++ b/legacy/ecore/src/lib/ecore/ecore_private.h @@ -444,7 +444,8 @@ void _ecore_fps_debug_init(void); void _ecore_fps_debug_shutdown(void); void _ecore_fps_debug_runtime_add(double t); - +int ecore_thread_init(void); +int ecore_thread_shutdown(void); extern int _ecore_fps_debug; extern double _ecore_loop_time; diff --git a/legacy/ecore/src/lib/ecore/ecore_thread.c b/legacy/ecore/src/lib/ecore/ecore_thread.c new file mode 100644 index 0000000000..4512ab0966 --- /dev/null +++ b/legacy/ecore/src/lib/ecore/ecore_thread.c @@ -0,0 +1,221 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef HAVE_EVIL +# include +#endif + +#include + +#include "ecore_private.h" +#include "Ecore.h" + +typedef struct _Ecore_Pthread_Worker Ecore_Pthread_Worker; +typedef struct _Ecore_Pthread Ecore_Pthread; + +struct _Ecore_Pthread_Worker +{ + void (*func_heavy)(void *data); + void (*func_end)(void *data); + + const void *data; +}; + +static int _ecore_thread_count_max = 0; +static int _ecore_thread_count = 0; + +static int _ecore_thread_init = 0; +static Eina_List *_ecore_thread = NULL; +static int ECORE_THREAD_PIPE_DEL = 0; +static Ecore_Event_Handler *del_handler = NULL; + +static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER; + +#ifdef BUILD_PTHREAD +static void +_ecore_thread_pipe_free(void *data __UNUSED__, void *event) +{ + Ecore_Pipe *p = event; + + ecore_pipe_del(p); +} + +static int +_ecore_thread_pipe_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__) +{ + /* This is a hack to delay pipe destruction until we are out of it's internal loop. */ + return 0; +} + +static void +_ecore_thread_end(pthread_t *thread) +{ + Ecore_Pipe *p; + + if (pthread_join(*thread, (void**) &p) != 0) + return ; + + ecore_event_add(ECORE_THREAD_PIPE_DEL, p, _ecore_thread_pipe_free, NULL); +} + +static void * +_ecore_thread_worker(Ecore_Pipe *p) +{ + Ecore_Pthread_Worker *work; + pthread_t *pth; + + pthread_mutex_lock(&_mutex); + _ecore_thread_count++; + pthread_mutex_unlock(&_mutex); + + on_error: + + while (_ecore_thread) + { + pthread_mutex_lock(&_mutex); + + if (!_ecore_thread) + { + pthread_mutex_unlock(&_mutex); + break; + } + + work = eina_list_data_get(_ecore_thread); + _ecore_thread = eina_list_remove_list(_ecore_thread, _ecore_thread); + + pthread_mutex_unlock(&_mutex); + + work->func_heavy((void*) work->data); + + ecore_pipe_write(p, &work, sizeof (Ecore_Pthread_Worker*)); + } + + pthread_mutex_lock(&_mutex); + if (_ecore_thread) + { + pthread_mutex_unlock(&_mutex); + goto on_error; + } + _ecore_thread_count--; + + pthread_mutex_unlock(&_mutex); + + work = malloc(sizeof (Ecore_Pthread_Worker) + sizeof (pthread_t)); + if (!work) return NULL; + + work->data = (void*) (work + 1); + work->func_heavy = NULL; + work->func_end = (void*) _ecore_thread_end; + + pth = (void*) work->data; + *pth = pthread_self(); + + ecore_pipe_write(p, &work, sizeof (Ecore_Pthread_Worker*)); + + return p; +} + +static void +_ecore_thread_handler(__UNUSED__ void *data, void *buffer, unsigned int nbyte) +{ + Ecore_Pthread_Worker *work; + + if (nbyte != sizeof (Ecore_Pthread_Worker*)) return ; + + work = *(Ecore_Pthread_Worker**)buffer; + + work->func_end((void*) work->data); + + free(work); +} +#endif + +int +ecore_thread_init(void) +{ + _ecore_thread_init++; + + if (_ecore_thread_init > 1) return _ecore_thread_init; + + _ecore_thread_count_max = eina_cpu_count(); + if (_ecore_thread_count_max < 0) + _ecore_thread_count_max = 1; + + ECORE_THREAD_PIPE_DEL = ecore_event_type_new(); + del_handler = ecore_event_handler_add(ECORE_THREAD_PIPE_DEL, _ecore_thread_pipe_del, NULL); + + return _ecore_thread_init; +} + +int +ecore_thread_shutdown(void) +{ + _ecore_thread_init--; + + if (!_ecore_thread_init) + { + /* FIXME: If function are still running in the background, should we kill them ? */ + ecore_event_handler_del(del_handler); + del_handler = NULL; + } + + return _ecore_thread_init; +} + +/* + * ecore_thread_run provide a facility for easily managing heavy task in a + * parallel thread. You should provide two function, the first one, func_heavy, + * that will do the heavy work in another thread (so you should not use the + * EFL in it except Eina if you are carefull), and the second one, func_end, + * that will be called in Ecore main loop when func_heavy is done. So you + * can use all the EFL inside this function. + * + * Be aware, that you can't make assumption on the result order of func_end + * after many call to ecore_thread_run, as we start as much thread as the + * host CPU can handle. + */ +EAPI Eina_Bool +ecore_thread_run(void (*func_heavy)(void *data), + void (*func_end)(void *data), + const void *data) +{ +#ifdef BUILD_PTHREAD + Ecore_Pthread_Worker *work; + Ecore_Pipe *p; + pthread_t thread; + + work = malloc(sizeof (Ecore_Pthread_Worker)); + if (!work) return EINA_FALSE; + + work->func_heavy = func_heavy; + work->func_end = func_end; + work->data = data; + + pthread_mutex_lock(&_mutex); + _ecore_thread = eina_list_append(_ecore_thread, work); + + if (_ecore_thread_count == _ecore_thread_count_max) + return EINA_TRUE; + + pthread_mutex_unlock(&_mutex); + + /* One more thread could be created. */ + p = ecore_pipe_add(_ecore_thread_handler, NULL); + + if (pthread_create(&thread, NULL, (void*) _ecore_thread_worker, p) == 0) + return EINA_TRUE; + + return EINA_FALSE; +#else + /* + If no thread and as we don't want to break app that rely on this + facility, we will lock the interface until we are done. + */ + func_heavy(data); + func_end(data); + + return EINA_TRUE; +#endif +} +