From 09748cfb154a314b3f98add4e0b98b30f9839df2 Mon Sep 17 00:00:00 2001 From: Gustavo Sverzut Barbieri Date: Mon, 31 Dec 2012 17:26:33 +0000 Subject: [PATCH] efl: beef thread documentation and error reporting. eina_thread_join() is nasty and didn't report errors :-( I'm using Eina_Error here, but it's global to the application and not thread-local. Maybe we should make eina_error_get() and eina_error_set() thread-local storage? SVN revision: 81936 --- src/lib/eina/eina_thread.c | 159 ++++++++++++++++++++++++++++----------------- src/lib/eina/eina_thread.h | 99 +++++++++++++++++++++++----- 2 files changed, 184 insertions(+), 74 deletions(-) diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c index 172c70f..0700650 100644 --- a/src/lib/eina/eina_thread.c +++ b/src/lib/eina/eina_thread.c @@ -23,19 +23,33 @@ #include #include "eina_config.h" +#include "eina_error.h" #include "eina_thread.h" #include "eina_sched.h" +#ifdef _WIN32 +# include "eina_list.h" +#endif -#ifdef EINA_HAVE_THREADS -# ifdef _WIN32_WCE +/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */ +#include "eina_safety_checks.h" +#include "eina_thread.h" -# elif defined(_WIN32) +EAPI Eina_Error EINA_ERROR_THREAD_CREATION_FAILED = 0; +EAPI Eina_Error EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES = 0; +EAPI Eina_Error EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS = 0; +EAPI Eina_Error EINA_ERROR_THREAD_JOIN_DEADLOCK = 0; +EAPI Eina_Error EINA_ERROR_THREAD_JOIN_INVALID = 0; -# include "eina_list.h" +static const char EINA_ERROR_THREAD_CREATION_FAILED_STR[] = "Generic error creating thread"; +static const char EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES_STR[] = "No resources to create thread"; +static const char EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS_STR[] = "No permissions to create thread"; +static const char EINA_ERROR_THREAD_JOIN_DEADLOCK_STR[] = "Deadlock detected"; +static const char EINA_ERROR_THREAD_JOIN_INVALID_STR[] = "Invalid thread to join"; -# define WIN32_LEAN_AND_MEAN -# include -# undef WIN32_LEAN_AND_MEAN +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# include +# undef WIN32_LEAN_AND_MEAN typedef struct _Eina_Thread_Win32 Eina_Thread_Win32; struct _Eina_Thread_Win32 @@ -66,8 +80,8 @@ _eina_thread_win32_find(Eina_Thread index) return NULL; } -static Eina_Thread -_eina_thread_win32_self(void) +static inline Eina_Thread +_eina_thread_self(void) { HANDLE t; Eina_Thread_Win32 *tw; @@ -82,8 +96,8 @@ _eina_thread_win32_self(void) return 0; } -static Eina_Bool -_eina_thread_win32_equal(Eina_Thread t1, Eina_Thread t2) +static inline Eina_Bool +_eina_thread_equal(Eina_Thread t1, Eina_Thread t2) { if (t1 == t2) return EINA_TRUE; return EINA_FALSE; @@ -99,8 +113,8 @@ _eina_thread_win32_cb(LPVOID lpParam) return 0; } -static Eina_Bool -_eina_thread_win32_create(Eina_Thread *t, +static inline Eina_Bool +_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), const void *data) @@ -127,7 +141,8 @@ _eina_thread_win32_create(Eina_Thread *t, if (!tw->thread) goto on_error; /* affinity is an hint, if we fail, we continue without */ - SetThreadAffinityMask(tw->thread, 1 << affinity); + if (affinity >= 0) + SetThreadAffinityMask(tw->thread, 1 << affinity); _thread_running = eina_list_append(_thread_running, tw); @@ -139,14 +154,18 @@ _eina_thread_win32_create(Eina_Thread *t, return EINA_FALSE; } -static void * -_eina_thread_win32_join(Eina_Thread t) +static inline void * +_eina_thread_join(Eina_Thread t) { Eina_Thread_Win32 *tw; void *ret; tw = _eina_thread_win32_find(t); - if (!tw) return NULL; + if (!tw) + { + eina_error_set(EINA_ERROR_THREAD_JOIN_INVALID); + return NULL; + } WaitForSingleObject(tw->thread, INFINITE); CloseHandle(tw->thread); @@ -164,69 +183,75 @@ _eina_thread_win32_join(Eina_Thread t) return ret; } -# define PHE(x, y) _eina_thread_win32_equal(x, y) -# define PHS() _eina_thread_win32_self() -# define PHC(x, a, f, d) _eina_thread_win32_create(x, a, f, d) -# define PHJ(x) _eina_thread_win32_join(x) -# define PHA(a) - -# else -# include - -# ifdef __linux__ -# include -# include -# include -# include -# include -# endif +#elif defined(EFL_HAVE_POSIX_THREADS) +# include +# include -static void * +static inline void * _eina_thread_join(Eina_Thread t) { void *ret = NULL; + int err = pthread_join(t, &ret); - if (!pthread_join(t, &ret)) + if (err == 0) return ret; + else if (err == EDEADLK) + eina_error_set(EINA_ERROR_THREAD_JOIN_DEADLOCK); + else + eina_error_set(EINA_ERROR_THREAD_JOIN_INVALID); + return NULL; } -static Eina_Bool +static inline Eina_Bool _eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data) { - Eina_Bool r; + int err; pthread_attr_t attr; -#ifdef EINA_HAVE_PTHREAD_AFFINITY - cpu_set_t cpu; - int cpunum; -#endif pthread_attr_init(&attr); -#ifdef EINA_HAVE_PTHREAD_AFFINITY if (affinity >= 0) { +#ifdef EINA_HAVE_PTHREAD_AFFINITY + cpu_set_t cpu; + int cpunum; + cpunum = eina_cpu_count(); CPU_ZERO(&cpu); CPU_SET(affinity % cpunum, &cpu); pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu); - } -#else - (void) affinity; #endif + } + /* setup initial locks */ - r = pthread_create(t, &attr, func, data) == 0; + err = pthread_create(t, &attr, func, data); pthread_attr_destroy(&attr); - return r; + if (err == 0) + return EINA_TRUE; + else if (err == EAGAIN) + eina_error_set(EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES); + else if (err == EPERM) + eina_error_set(EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS); + else + eina_error_set(EINA_ERROR_THREAD_CREATION_FAILED); + + return EINA_FALSE; +} + +static inline Eina_Bool +_eina_thread_equal(Eina_Thread t1, Eina_Thread t2) +{ + return pthread_equal(t1, t2); } -# define PHE(x, y) pthread_equal(x, y) -# define PHS() pthread_self() -# define PHC(x, a, f, d) _eina_thread_create(x, a, f, d) -# define PHJ(x) _eina_thread_join(x) +static inline Eina_Thread +_eina_thread_self(void) +{ + return pthread_self(); +} -# endif #else # error "Not supported any more" #endif @@ -241,8 +266,6 @@ struct _Eina_Thread_Call int affinity; }; -#include "eina_thread.h" - static void * _eina_internal_call(void *context) { @@ -264,13 +287,13 @@ _eina_internal_call(void *context) EAPI Eina_Thread eina_thread_self(void) { - return PHS(); + return _eina_thread_self(); } EAPI Eina_Bool eina_thread_equal(Eina_Thread t1, Eina_Thread t2) { - return !!(PHE(t1, t2)); + return !!_eina_thread_equal(t1, t2); } EAPI Eina_Bool @@ -280,18 +303,29 @@ eina_thread_create(Eina_Thread *t, { Eina_Thread_Call *c; + EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE); + + eina_error_set(0); + c = malloc(sizeof (Eina_Thread_Call)); - if (!c) return EINA_FALSE; + if (!c) + { + eina_error_set(EINA_ERROR_OUT_OF_MEMORY); + return EINA_FALSE; + } c->func = func; c->data = data; c->prio = prio; c->affinity = affinity; - if (PHC(t, affinity, _eina_internal_call, c)) + if (_eina_thread_create(t, affinity, _eina_internal_call, c)) return EINA_TRUE; free(c); + if (eina_error_get() == 0) + eina_error_set(EINA_ERROR_THREAD_CREATION_FAILED); return EINA_FALSE; } @@ -299,12 +333,19 @@ eina_thread_create(Eina_Thread *t, EAPI void * eina_thread_join(Eina_Thread t) { - return PHJ(t); + eina_error_set(0); + return _eina_thread_join(t); } Eina_Bool eina_thread_init(void) { + EINA_ERROR_THREAD_CREATION_FAILED = eina_error_msg_static_register(EINA_ERROR_THREAD_CREATION_FAILED_STR); + EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES = eina_error_msg_static_register(EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES_STR); + EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS = eina_error_msg_static_register(EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS_STR); + EINA_ERROR_THREAD_JOIN_DEADLOCK = eina_error_msg_static_register(EINA_ERROR_THREAD_JOIN_DEADLOCK_STR); + EINA_ERROR_THREAD_JOIN_INVALID = eina_error_msg_static_register(EINA_ERROR_THREAD_JOIN_INVALID_STR); + return EINA_TRUE; } diff --git a/src/lib/eina/eina_thread.h b/src/lib/eina/eina_thread.h index 85bcb33..6751a6e 100644 --- a/src/lib/eina/eina_thread.h +++ b/src/lib/eina/eina_thread.h @@ -32,27 +32,57 @@ /** * @defgroup Eina_Thread_Group Thread * + * Abstracts platform threads, providing an uniform API. It's modeled + * after POSIX THREADS (pthreads), on Linux they are almost 1:1 + * mapping. + * + * @see @ref Eina_Lock_Group for mutex/locking abstraction. + * + * @since 1.8 * @{ */ -#ifdef EINA_HAVE_THREADS -# ifdef _WIN32_WCE +/** + * @var EINA_ERROR_THREAD_CREATION_FAILED + * Generic error happened and thread couldn't be created. + * @since 1.8 + */ +EAPI extern Eina_Error EINA_ERROR_THREAD_CREATION_FAILED; -typedef unsigned long int Eina_Thread; +/** + * @var EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES + * System lacked resources to create thread. + * @since 1.8 + */ +EAPI extern Eina_Error EINA_ERROR_THREAD_CREATION_FAILED_RESOURCES; -# elif defined(_WIN32) +/** + * @var EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS + * System lacked permissions to create thread. + * @since 1.8 + */ +EAPI extern Eina_Error EINA_ERROR_THREAD_CREATION_FAILED_PERMISSIONS; -typedef unsigned long int Eina_Thread; +/** + * @var EINA_ERROR_THREAD_JOIN_DEADLOCK + * The system has detected a deadlock situation where both threads + * would wait each other. Or the thread wanted to wait for itself. + * @since 1.8 + */ +EAPI extern Eina_Error EINA_ERROR_THREAD_JOIN_DEADLOCK; -# else -# include +/** + * @var EINA_ERROR_THREAD_JOIN_INVALID + * One of the following happened: + * @li thread is not a joinable. + * @li thread does not exist. + * @li another thread is already waiting for that thread. + * @since 1.8 + */ +EAPI extern Eina_Error EINA_ERROR_THREAD_JOIN_INVALID; -typedef pthread_t Eina_Thread; -# endif -#else -# error "Build without thread is not supported any more" -#endif +typedef unsigned long int Eina_Thread; typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t); @@ -64,11 +94,50 @@ typedef enum _Eina_Thread_Priority EINA_THREAD_IDLE } Eina_Thread_Priority; -EAPI Eina_Thread eina_thread_self(void); -EAPI Eina_Bool eina_thread_equal(Eina_Thread t1, Eina_Thread t2); +/** + * Return identifier of the current thread. + * @return identifier of current thread. + * @since 1.8 + */ +EAPI Eina_Thread eina_thread_self(void) EINA_WARN_UNUSED_RESULT; + +/** + * Check if two thread identifiers are the same. + * @param t1 first thread identifier to compare. + * @param t2 second thread identifier to compare. + * @return #EINA_TRUE if they are equal, #EINA_FALSE otherwise. + * @since 1.8 + */ +EAPI Eina_Bool eina_thread_equal(Eina_Thread t1, Eina_Thread t2) EINA_WARN_UNUSED_RESULT; + +/** + * Create a new thread, setting its priority and affinity. + * + * @param t[out] where to return the thread identifier. Must @b not be @c NULL. + * @param prio thread priority to use, usually #EINA_THREAD_BACKGROUND + * @param affinity thread affinity to use. To not set affinity use @c -1. + * @param func function to run in the thread. Must @b not be @c NULL. + * @param data context data to provide to @a func as first argument. + * @return #EINA_TRUE if thread was created, #EINA_FALSE on errors. + * @since 1.8 + */ EAPI Eina_Bool eina_thread_create(Eina_Thread *t, Eina_Thread_Priority prio, int affinity, - Eina_Thread_Cb func, const void *data); + Eina_Thread_Cb func, const void *data) EINA_ARG_NONNULL(1, 4) EINA_WARN_UNUSED_RESULT; + +/** + * Join a currently running thread, waiting until it finishes. + * + * This function will block the current thread until @a t + * finishes. The returned value is the one returned by @a t @c func() + * and may be @c NULL on errors. See @ref Eina_Error_Group to identify + * problems. + * + * @param t thread identifier to wait. + * @return value returned by @a t creation function @c func() or + * @c NULL on errors. Check error with @ref Eina_Error_Group. + * @since 1.8 + */ EAPI void *eina_thread_join(Eina_Thread t); /** -- 2.7.4