#include <stdlib.h>
#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 <windows.h>
-# undef WIN32_LEAN_AND_MEAN
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# undef WIN32_LEAN_AND_MEAN
typedef struct _Eina_Thread_Win32 Eina_Thread_Win32;
struct _Eina_Thread_Win32
return NULL;
}
-static Eina_Thread
-_eina_thread_win32_self(void)
+static inline Eina_Thread
+_eina_thread_self(void)
{
HANDLE t;
Eina_Thread_Win32 *tw;
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;
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)
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);
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);
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 <pthread.h>
-
-# ifdef __linux__
-# include <sched.h>
-# include <sys/resource.h>
-# include <unistd.h>
-# include <sys/syscall.h>
-# include <errno.h>
-# endif
+#elif defined(EFL_HAVE_POSIX_THREADS)
+# include <pthread.h>
+# include <errno.h>
-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
int affinity;
};
-#include "eina_thread.h"
-
static void *
_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
{
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;
}
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;
}
/**
* @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 <pthread.h>
+/**
+ * @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);
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);
/**