efl: beef thread documentation and error reporting.
authorGustavo Sverzut Barbieri <barbieri@gmail.com>
Mon, 31 Dec 2012 17:26:33 +0000 (17:26 +0000)
committerGustavo Sverzut Barbieri <barbieri@gmail.com>
Mon, 31 Dec 2012 17:26:33 +0000 (17:26 +0000)
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
src/lib/eina/eina_thread.h

index 172c70f..0700650 100644 (file)
 #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
@@ -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 <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
@@ -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;
 }
 
index 85bcb33..6751a6e 100644 (file)
 /**
  * @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);
 
@@ -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);
 
 /**