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 +} +