forked from enlightenment/efl
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
This commit is contained in:
parent
4668d04f04
commit
09748cfb15
|
@ -23,19 +23,33 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "eina_config.h"
|
#include "eina_config.h"
|
||||||
|
#include "eina_error.h"
|
||||||
#include "eina_thread.h"
|
#include "eina_thread.h"
|
||||||
#include "eina_sched.h"
|
#include "eina_sched.h"
|
||||||
|
#ifdef _WIN32
|
||||||
|
# include "eina_list.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef EINA_HAVE_THREADS
|
/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
|
||||||
# ifdef _WIN32_WCE
|
#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
|
#ifdef _WIN32
|
||||||
# include <windows.h>
|
# define WIN32_LEAN_AND_MEAN
|
||||||
# undef WIN32_LEAN_AND_MEAN
|
# include <windows.h>
|
||||||
|
# undef WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
typedef struct _Eina_Thread_Win32 Eina_Thread_Win32;
|
typedef struct _Eina_Thread_Win32 Eina_Thread_Win32;
|
||||||
struct _Eina_Thread_Win32
|
struct _Eina_Thread_Win32
|
||||||
|
@ -66,8 +80,8 @@ _eina_thread_win32_find(Eina_Thread index)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Thread
|
static inline Eina_Thread
|
||||||
_eina_thread_win32_self(void)
|
_eina_thread_self(void)
|
||||||
{
|
{
|
||||||
HANDLE t;
|
HANDLE t;
|
||||||
Eina_Thread_Win32 *tw;
|
Eina_Thread_Win32 *tw;
|
||||||
|
@ -82,8 +96,8 @@ _eina_thread_win32_self(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Bool
|
static inline Eina_Bool
|
||||||
_eina_thread_win32_equal(Eina_Thread t1, Eina_Thread t2)
|
_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
||||||
{
|
{
|
||||||
if (t1 == t2) return EINA_TRUE;
|
if (t1 == t2) return EINA_TRUE;
|
||||||
return EINA_FALSE;
|
return EINA_FALSE;
|
||||||
|
@ -99,8 +113,8 @@ _eina_thread_win32_cb(LPVOID lpParam)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Bool
|
static inline Eina_Bool
|
||||||
_eina_thread_win32_create(Eina_Thread *t,
|
_eina_thread_create(Eina_Thread *t,
|
||||||
int affinity,
|
int affinity,
|
||||||
void *(*func)(void *data),
|
void *(*func)(void *data),
|
||||||
const void *data)
|
const void *data)
|
||||||
|
@ -127,7 +141,8 @@ _eina_thread_win32_create(Eina_Thread *t,
|
||||||
if (!tw->thread) goto on_error;
|
if (!tw->thread) goto on_error;
|
||||||
|
|
||||||
/* affinity is an hint, if we fail, we continue without */
|
/* 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);
|
_thread_running = eina_list_append(_thread_running, tw);
|
||||||
|
|
||||||
|
@ -139,14 +154,18 @@ _eina_thread_win32_create(Eina_Thread *t,
|
||||||
return EINA_FALSE;
|
return EINA_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static inline void *
|
||||||
_eina_thread_win32_join(Eina_Thread t)
|
_eina_thread_join(Eina_Thread t)
|
||||||
{
|
{
|
||||||
Eina_Thread_Win32 *tw;
|
Eina_Thread_Win32 *tw;
|
||||||
void *ret;
|
void *ret;
|
||||||
|
|
||||||
tw = _eina_thread_win32_find(t);
|
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);
|
WaitForSingleObject(tw->thread, INFINITE);
|
||||||
CloseHandle(tw->thread);
|
CloseHandle(tw->thread);
|
||||||
|
@ -164,69 +183,75 @@ _eina_thread_win32_join(Eina_Thread t)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define PHE(x, y) _eina_thread_win32_equal(x, y)
|
#elif defined(EFL_HAVE_POSIX_THREADS)
|
||||||
# define PHS() _eina_thread_win32_self()
|
# include <pthread.h>
|
||||||
# define PHC(x, a, f, d) _eina_thread_win32_create(x, a, f, d)
|
# include <errno.h>
|
||||||
# define PHJ(x) _eina_thread_win32_join(x)
|
|
||||||
# define PHA(a)
|
|
||||||
|
|
||||||
# else
|
static inline void *
|
||||||
# include <pthread.h>
|
|
||||||
|
|
||||||
# ifdef __linux__
|
|
||||||
# include <sched.h>
|
|
||||||
# include <sys/resource.h>
|
|
||||||
# include <unistd.h>
|
|
||||||
# include <sys/syscall.h>
|
|
||||||
# include <errno.h>
|
|
||||||
# endif
|
|
||||||
|
|
||||||
static void *
|
|
||||||
_eina_thread_join(Eina_Thread t)
|
_eina_thread_join(Eina_Thread t)
|
||||||
{
|
{
|
||||||
void *ret = NULL;
|
void *ret = NULL;
|
||||||
|
int err = pthread_join(t, &ret);
|
||||||
|
|
||||||
if (!pthread_join(t, &ret))
|
if (err == 0)
|
||||||
return ret;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Eina_Bool
|
static inline Eina_Bool
|
||||||
_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
|
_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
|
||||||
{
|
{
|
||||||
Eina_Bool r;
|
int err;
|
||||||
pthread_attr_t attr;
|
pthread_attr_t attr;
|
||||||
#ifdef EINA_HAVE_PTHREAD_AFFINITY
|
|
||||||
cpu_set_t cpu;
|
|
||||||
int cpunum;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pthread_attr_init(&attr);
|
pthread_attr_init(&attr);
|
||||||
#ifdef EINA_HAVE_PTHREAD_AFFINITY
|
|
||||||
if (affinity >= 0)
|
if (affinity >= 0)
|
||||||
{
|
{
|
||||||
|
#ifdef EINA_HAVE_PTHREAD_AFFINITY
|
||||||
|
cpu_set_t cpu;
|
||||||
|
int cpunum;
|
||||||
|
|
||||||
cpunum = eina_cpu_count();
|
cpunum = eina_cpu_count();
|
||||||
|
|
||||||
CPU_ZERO(&cpu);
|
CPU_ZERO(&cpu);
|
||||||
CPU_SET(affinity % cpunum, &cpu);
|
CPU_SET(affinity % cpunum, &cpu);
|
||||||
pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
|
pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
|
||||||
}
|
|
||||||
#else
|
|
||||||
(void) affinity;
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/* setup initial locks */
|
/* setup initial locks */
|
||||||
r = pthread_create(t, &attr, func, data) == 0;
|
err = pthread_create(t, &attr, func, data);
|
||||||
pthread_attr_destroy(&attr);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
# define PHE(x, y) pthread_equal(x, y)
|
static inline Eina_Bool
|
||||||
# define PHS() pthread_self()
|
_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
||||||
# define PHC(x, a, f, d) _eina_thread_create(x, a, f, d)
|
{
|
||||||
# define PHJ(x) _eina_thread_join(x)
|
return pthread_equal(t1, t2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline Eina_Thread
|
||||||
|
_eina_thread_self(void)
|
||||||
|
{
|
||||||
|
return pthread_self();
|
||||||
|
}
|
||||||
|
|
||||||
# endif
|
|
||||||
#else
|
#else
|
||||||
# error "Not supported any more"
|
# error "Not supported any more"
|
||||||
#endif
|
#endif
|
||||||
|
@ -241,8 +266,6 @@ struct _Eina_Thread_Call
|
||||||
int affinity;
|
int affinity;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "eina_thread.h"
|
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
_eina_internal_call(void *context)
|
_eina_internal_call(void *context)
|
||||||
{
|
{
|
||||||
|
@ -264,13 +287,13 @@ _eina_internal_call(void *context)
|
||||||
EAPI Eina_Thread
|
EAPI Eina_Thread
|
||||||
eina_thread_self(void)
|
eina_thread_self(void)
|
||||||
{
|
{
|
||||||
return PHS();
|
return _eina_thread_self();
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI Eina_Bool
|
EAPI Eina_Bool
|
||||||
eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
|
||||||
{
|
{
|
||||||
return !!(PHE(t1, t2));
|
return !!_eina_thread_equal(t1, t2);
|
||||||
}
|
}
|
||||||
|
|
||||||
EAPI Eina_Bool
|
EAPI Eina_Bool
|
||||||
|
@ -280,18 +303,29 @@ eina_thread_create(Eina_Thread *t,
|
||||||
{
|
{
|
||||||
Eina_Thread_Call *c;
|
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));
|
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->func = func;
|
||||||
c->data = data;
|
c->data = data;
|
||||||
c->prio = prio;
|
c->prio = prio;
|
||||||
c->affinity = affinity;
|
c->affinity = affinity;
|
||||||
|
|
||||||
if (PHC(t, affinity, _eina_internal_call, c))
|
if (_eina_thread_create(t, affinity, _eina_internal_call, c))
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
|
|
||||||
free(c);
|
free(c);
|
||||||
|
if (eina_error_get() == 0)
|
||||||
|
eina_error_set(EINA_ERROR_THREAD_CREATION_FAILED);
|
||||||
|
|
||||||
return EINA_FALSE;
|
return EINA_FALSE;
|
||||||
}
|
}
|
||||||
|
@ -299,12 +333,19 @@ eina_thread_create(Eina_Thread *t,
|
||||||
EAPI void *
|
EAPI void *
|
||||||
eina_thread_join(Eina_Thread t)
|
eina_thread_join(Eina_Thread t)
|
||||||
{
|
{
|
||||||
return PHJ(t);
|
eina_error_set(0);
|
||||||
|
return _eina_thread_join(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
Eina_Bool
|
Eina_Bool
|
||||||
eina_thread_init(void)
|
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;
|
return EINA_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,28 +32,58 @@
|
||||||
/**
|
/**
|
||||||
* @defgroup Eina_Thread_Group Thread
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 unsigned long int Eina_Thread;
|
typedef unsigned long int Eina_Thread;
|
||||||
|
|
||||||
# elif defined(_WIN32)
|
|
||||||
|
|
||||||
typedef unsigned long int Eina_Thread;
|
|
||||||
|
|
||||||
# else
|
|
||||||
# include <pthread.h>
|
|
||||||
|
|
||||||
typedef pthread_t Eina_Thread;
|
|
||||||
|
|
||||||
# endif
|
|
||||||
#else
|
|
||||||
# error "Build without thread is not supported any more"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
|
typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
|
||||||
|
|
||||||
typedef enum _Eina_Thread_Priority
|
typedef enum _Eina_Thread_Priority
|
||||||
|
@ -64,11 +94,50 @@ typedef enum _Eina_Thread_Priority
|
||||||
EINA_THREAD_IDLE
|
EINA_THREAD_IDLE
|
||||||
} Eina_Thread_Priority;
|
} 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,
|
EAPI Eina_Bool eina_thread_create(Eina_Thread *t,
|
||||||
Eina_Thread_Priority prio, int affinity,
|
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);
|
EAPI void *eina_thread_join(Eina_Thread t);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue