summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWander Lairson Costa <wander.lairson@gmail.com>2020-12-15 16:13:48 -0300
committerFelipe Magno de Almeida <felipe@expertise.dev>2020-12-15 17:20:24 -0300
commit6bc474865731e5ed3fc9040c85ff2ff75ea89d0b (patch)
tree43a1e4f66f6514ce8a7095dfab2be9130b1004f1
parent886723d47b27d61bae31b767b308b75c0d909ee9 (diff)
downloadefl-6bc474865731e5ed3fc9040c85ff2ff75ea89d0b.tar.gz
eina: Implement Eina_Thread for native windows
The implementation design respects the fact that Eina_Thread is an uintptr_t. Thus we allocate the thread struct in the heap and return a pointer to it. As such, we store the created thread structure in the target thread TLS slot. For threads that were not created through eina API, in eina_thread_self we allocate a new structure, push it to the TLS slot and mark it to be freed on thread exit. Reviewers: jptiz, walac, vtorri, woohyun, lucas Reviewed By: jptiz, cedric Subscribers: raster, cedric, #reviewers, #committers, lucas Tags: #efl Differential Revision: https://phab.enlightenment.org/D12037
-rw-r--r--header_checks/meson.build3
-rw-r--r--src/lib/eina/Eina.h1
-rw-r--r--src/lib/eina/eina_sched.c84
-rw-r--r--src/lib/eina/eina_sched.h53
-rw-r--r--src/lib/eina/eina_thread.c295
-rw-r--r--src/lib/eina/eina_thread.h36
-rw-r--r--src/lib/eina/eina_thread_posix.c368
-rw-r--r--src/lib/eina/eina_thread_win32.c397
-rw-r--r--src/lib/eina/eina_win32_dllmain.c18
-rw-r--r--src/lib/eina/meson.build6
-rw-r--r--src/tests/eina/eina_suite.c1
-rw-r--r--src/tests/eina/eina_suite.h1
-rw-r--r--src/tests/eina/eina_test_thread.c124
-rw-r--r--src/tests/eina/meson.build1
14 files changed, 956 insertions, 432 deletions
diff --git a/header_checks/meson.build b/header_checks/meson.build
index a2a0e097eb..aaae83d62e 100644
--- a/header_checks/meson.build
+++ b/header_checks/meson.build
@@ -53,7 +53,8 @@ header_checks = [
'features.h',
'langinfo.h',
'locale.h',
- 'crt_externs.h'
+ 'crt_externs.h',
+ 'pthread.h',
]
#### The below is logically broken
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index 7bf09b739c..c7fd41eded 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -241,7 +241,6 @@ extern "C" {
#include <eina_benchmark.h>
#include <eina_convert.h>
#include <eina_cpu.h>
-#include <eina_sched.h>
#include <eina_tiler.h>
#include <eina_hamster.h>
#include <eina_matrixsparse.h>
diff --git a/src/lib/eina/eina_sched.c b/src/lib/eina/eina_sched.c
deleted file mode 100644
index 3864054712..0000000000
--- a/src/lib/eina/eina_sched.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2010 ProFUSION embedded systems
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
-
-#include <pthread.h>
-#ifdef __linux__
-# include <sched.h>
-# include <sys/time.h>
-# include <sys/resource.h>
-# include <errno.h>
-#endif
-
-#include "eina_sched.h"
-#include "eina_log.h"
-
-#define RTNICENESS 1
-#define NICENESS 5
-
-EINA_API void
-eina_sched_prio_drop(void)
-{
- struct sched_param param;
- int pol, ret;
- pthread_t pthread_id;
-
- pthread_id = pthread_self();
- ret = pthread_getschedparam(pthread_id, &pol, &param);
- if (ret)
- {
- EINA_LOG_ERR("Unable to query sched parameters");
- return;
- }
-
- if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO))
- {
- param.sched_priority -= RTNICENESS;
-
- /* We don't change the policy */
- if (param.sched_priority < 1)
- {
- EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
- param.sched_priority = 1;
- }
-
- pthread_setschedparam(pthread_id, pol, &param);
- }
-# ifdef __linux__
- else
- {
- int prio;
- errno = 0;
- prio = getpriority(PRIO_PROCESS, 0);
- if (errno == 0)
- {
- prio += NICENESS;
- if (prio > 19)
- {
- EINA_LOG_INFO("Max niceness reached; keeping max (19)");
- prio = 19;
- }
-
- setpriority(PRIO_PROCESS, 0, prio);
- }
- }
-# endif
-}
diff --git a/src/lib/eina/eina_sched.h b/src/lib/eina/eina_sched.h
deleted file mode 100644
index 77d90a31e4..0000000000
--- a/src/lib/eina/eina_sched.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/* EINA - EFL data type library
- * Copyright (C) 2010 ProFUSION embedded systems
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library;
- * if not, see <http://www.gnu.org/licenses/>.
- */
-
-/**
- * @defgroup Schedule Schedule
- * @ingroup Eina_Tools_Group
- *
- * @{
- *
- * TODO: description
- *
- */
-
-#ifndef EINA_SCHED_H_
-#define EINA_SCHED_H_
-
-#include "eina_types.h"
-
-
-/**
- * @brief Lowers the priority of the current thread.
- *
- * @details It's used by worker threads so that they use up the background CPU and do not stall
- * the main thread. If the current thread is running with real-time priority, we
- * decrease our priority by @c RTNICENESS. This is done in a portable way.
- *
- * Otherwise, (we are running with the SCHED_OTHER policy) there's no portable way to
- * set the nice level on the current thread. In Linux, it does work and it's the
- * only one that is implemented as of now. In this case, the nice level is
- * incremented on this thread by @c NICENESS.
- */
-EINA_API void eina_sched_prio_drop(void);
-
-/**
- * @}
- */
-
-#endif /* EINA_SCHED_H_ */
diff --git a/src/lib/eina/eina_thread.c b/src/lib/eina/eina_thread.c
index 75623edb0a..abb04a1bf5 100644
--- a/src/lib/eina/eina_thread.c
+++ b/src/lib/eina/eina_thread.c
@@ -20,284 +20,15 @@
# include "config.h"
#endif
-#include <stdlib.h>
+#include <stddef.h>
-#include "eina_config.h"
-#include "eina_lock.h" /* it will include pthread.h with proper flags */
-#include "eina_thread.h"
-#include "eina_sched.h"
-#include "eina_cpu.h"
-
-/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
-#include "eina_safety_checks.h"
-
-#include "eina_debug_private.h"
-
-#include <pthread.h>
-#include <errno.h>
-#ifndef _WIN32
-# include <signal.h>
-#endif
-# include <string.h>
-
-#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
-#ifndef __linux__
-#include <pthread_np.h>
-#define cpu_set_t cpuset_t
-#endif
-#endif
-
-static inline void *
-_eina_thread_join(Eina_Thread t)
-{
- void *ret = NULL;
- int err = pthread_join((pthread_t)t, &ret);
-
- if (err == 0) return ret;
- return NULL;
-}
-
-static inline Eina_Bool
-_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
-{
- int err;
- pthread_attr_t attr;
-#ifndef _WIN32
- sigset_t oldset, newset;
-#endif
-
- if (pthread_attr_init(&attr) != 0)
- {
- return EINA_FALSE;
- }
- if (affinity >= 0)
- {
-#ifdef EINA_HAVE_PTHREAD_AFFINITY
- cpu_set_t cpu;
-
- CPU_ZERO(&cpu);
- CPU_SET(affinity, &cpu);
- pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
-#endif
- }
-
- /* setup initial locks */
-#ifndef _WIN32
- sigemptyset(&newset);
- sigaddset(&newset, SIGPIPE);
- sigaddset(&newset, SIGALRM);
- sigaddset(&newset, SIGCHLD);
- sigaddset(&newset, SIGUSR1);
- sigaddset(&newset, SIGUSR2);
- sigaddset(&newset, SIGHUP);
- sigaddset(&newset, SIGQUIT);
- sigaddset(&newset, SIGINT);
- sigaddset(&newset, SIGTERM);
-# ifdef SIGPWR
- sigaddset(&newset, SIGPWR);
-# endif
- pthread_sigmask(SIG_BLOCK, &newset, &oldset);
-#endif
- err = pthread_create((pthread_t *)t, &attr, func, data);
-#ifndef _WIN32
- pthread_sigmask(SIG_SETMASK, &oldset, NULL);
-#endif
- pthread_attr_destroy(&attr);
-
- if (err == 0) return EINA_TRUE;
-
- return EINA_FALSE;
-}
-
-static inline Eina_Bool
-_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
-{
- return pthread_equal((pthread_t)t1, (pthread_t)t2);
-}
-
-static inline Eina_Thread
-_eina_thread_self(void)
-{
- return (Eina_Thread)pthread_self();
-}
-
-
-typedef struct _Eina_Thread_Call Eina_Thread_Call;
-struct _Eina_Thread_Call
-{
- Eina_Thread_Cb func;
- const void *data;
-
- Eina_Thread_Priority prio;
- int affinity;
-};
-
-static void *
-_eina_internal_call(void *context)
-{
- Eina_Thread_Call *c = context;
- void *r;
- pthread_t self;
-
- // Default this thread to not cancellable as per Eina documentation
- eina_thread_cancellable_set(EINA_FALSE, NULL);
-
- EINA_THREAD_CLEANUP_PUSH(free, c);
-
- self = pthread_self();
-
- if (c->prio == EINA_THREAD_IDLE)
- {
- struct sched_param params;
- int min;
-#ifdef SCHED_IDLE
- int pol = SCHED_IDLE;
-#else
- int pol;
- pthread_getschedparam(self, &pol, &params);
+#ifdef HAVE_PTHREAD_H
+# include <pthread.h>
#endif
- min = sched_get_priority_min(pol);
- params.sched_priority = min;
- pthread_setschedparam(self, pol, &params);
- }
- else if (c->prio == EINA_THREAD_BACKGROUND)
- {
- struct sched_param params;
- int min, max;
-#ifdef SCHED_BATCH
- int pol = SCHED_BATCH;
-#else
- int pol;
- pthread_getschedparam(self, &pol, &params);
-#endif
- min = sched_get_priority_min(pol);
- max = sched_get_priority_max(pol);
- params.sched_priority = (max - min) / 2;
- pthread_setschedparam(self, pol, &params);
- }
-// do nothing for normal
-// else if (c->prio == EINA_THREAD_NORMAL)
-// {
-// }
- else if (c->prio == EINA_THREAD_URGENT)
- {
- struct sched_param params;
- int max, pol;
-
- pthread_getschedparam(self, &pol, &params);
- max = sched_get_priority_max(pol);
- params.sched_priority += 5;
- if (params.sched_priority > max) params.sched_priority = max;
- pthread_setschedparam(self, pol, &params);
- }
-
- _eina_debug_thread_add(&self);
- EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
- r = c->func((void*) c->data, eina_thread_self());
- EINA_THREAD_CLEANUP_POP(EINA_TRUE);
- EINA_THREAD_CLEANUP_POP(EINA_TRUE);
- return r;
-}
-
-EINA_API Eina_Thread
-eina_thread_self(void)
-{
- return _eina_thread_self();
-}
-
-EINA_API Eina_Bool
-eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
-{
- return !!_eina_thread_equal(t1, t2);
-}
-
-EINA_API Eina_Bool
-eina_thread_create(Eina_Thread *t,
- Eina_Thread_Priority prio, int affinity,
- Eina_Thread_Cb func, const void *data)
-{
- Eina_Thread_Call *c;
-
- EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
- EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
-
- c = malloc(sizeof (Eina_Thread_Call));
- if (!c) return EINA_FALSE;
-
- c->func = func;
- c->data = data;
- c->prio = prio;
- c->affinity = affinity;
-
- // valgrind complains c is lost - but it's not - it is handed to the
- // child thread to be freed when c->func returns in _eina_internal_call().
- if (_eina_thread_create(t, affinity, _eina_internal_call, c))
- return EINA_TRUE;
-
- free(c);
- return EINA_FALSE;
-}
-
-EINA_API void *
-eina_thread_join(Eina_Thread t)
-{
- return _eina_thread_join(t);
-}
-
-EINA_API Eina_Bool
-eina_thread_name_set(Eina_Thread t, const char *name)
-{
-#ifdef EINA_HAVE_PTHREAD_SETNAME
- char buf[16];
- if (name)
- {
- strncpy(buf, name, 15);
- buf[15] = 0;
- }
- else buf[0] = 0;
-#ifndef __linux__
- pthread_set_name_np((pthread_t)t, buf);
- return EINA_TRUE;
-#else
- if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE;
-#endif
-#else
- (void)t;
- (void)name;
-#endif
- return EINA_FALSE;
-}
-
-EINA_API Eina_Bool
-eina_thread_cancel(Eina_Thread t)
-{
- if (!t) return EINA_FALSE;
- return pthread_cancel((pthread_t)t) == 0;
-}
-
-EINA_API Eina_Bool
-eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
-{
- int state = cancellable ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
- int old = 0;
- int r;
-
- /* enforce deferred in case users changed to asynchronous themselves */
- pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
-
- r = pthread_setcancelstate(state, &old);
- if (was_cancellable && r == 0)
- *was_cancellable = (old == PTHREAD_CANCEL_ENABLE);
-
- return r == 0;
-}
-
-EINA_API void
-eina_thread_cancel_checkpoint(void)
-{
- pthread_testcancel();
-}
+#include "eina_types.h"
+#include "eina_config.h"
+#include "eina_thread.h"
EINA_API void *
eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data)
@@ -312,17 +43,3 @@ eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb clea
eina_thread_cancellable_set(old, NULL);
return ret;
}
-
-EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
-
-Eina_Bool
-eina_thread_init(void)
-{
- return EINA_TRUE;
-}
-
-Eina_Bool
-eina_thread_shutdown(void)
-{
- return EINA_TRUE;
-}
diff --git a/src/lib/eina/eina_thread.h b/src/lib/eina/eina_thread.h
index d0ca8138c2..9c0b93bf1f 100644
--- a/src/lib/eina/eina_thread.h
+++ b/src/lib/eina/eina_thread.h
@@ -52,6 +52,12 @@ typedef uintptr_t Eina_Thread;
typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
/**
+ * @typedef Eina_Thread_Cleanup_Cb
+ * Type for the definition of a thread cleanup function
+ */
+typedef void (*Eina_Thread_Cleanup_Cb) (void *data);
+
+/**
* @typedef Eina_Thread_Priority
* Type to enumerate different thread priorities
*/
@@ -248,8 +254,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
*
* @since 1.19
*/
+#ifdef _WIN32
+EINA_API Eina_Bool
+eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data);
+
+#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
+ eina_thread_cleanup_push(cleanup, data)
+#else
#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
pthread_cleanup_push(cleanup, data)
+#endif
/**
* @def EINA_THREAD_CLEANUP_POP(exec_cleanup)
@@ -278,8 +292,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
*
* @since 1.19
*/
+#ifdef _WIN32
+EINA_API void
+eina_thread_cleanup_pop(int execute);
+
+#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
+ eina_thread_cleanup_pop(exec_cleanup)
+#else
#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
pthread_cleanup_pop(exec_cleanup)
+#endif
/**
* @typedef Eina_Thread_Cancellable_Run_Cb
@@ -334,6 +356,20 @@ typedef void *(*Eina_Thread_Cancellable_Run_Cb)(void *data);
EINA_API void *eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data);
/**
+ * @brief Lowers the priority of the current thread.
+ *
+ * @details It's used by worker threads so that they use up the background CPU and do not stall
+ * the main thread. If the current thread is running with real-time priority, we
+ * decrease our priority by @c RTNICENESS. This is done in a portable way.
+ *
+ * Otherwise, (we are running with the SCHED_OTHER policy) there's no portable way to
+ * set the nice level on the current thread. In Linux, it does work and it's the
+ * only one that is implemented as of now. In this case, the nice level is
+ * incremented on this thread by @c NICENESS.
+ */
+EINA_API void eina_sched_prio_drop(void);
+
+/**
* @}
*/
diff --git a/src/lib/eina/eina_thread_posix.c b/src/lib/eina/eina_thread_posix.c
new file mode 100644
index 0000000000..da793bd48d
--- /dev/null
+++ b/src/lib/eina/eina_thread_posix.c
@@ -0,0 +1,368 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2012 Cedric Bail
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+
+#include "eina_config.h"
+#include "eina_lock.h" /* it will include pthread.h with proper flags */
+#include "eina_thread.h"
+#include "eina_cpu.h"
+
+/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
+#include "eina_safety_checks.h"
+
+#include "eina_debug_private.h"
+
+#include <pthread.h>
+#include <errno.h>
+#ifndef _WIN32
+# include <signal.h>
+#endif
+# include <string.h>
+
+#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
+#ifndef __linux__
+#include <pthread_np.h>
+#define cpu_set_t cpuset_t
+#endif
+#endif
+
+#ifdef __linux__
+# include <sched.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#include "eina_log.h"
+
+#define RTNICENESS 1
+#define NICENESS 5
+
+
+static inline void *
+_eina_thread_join(Eina_Thread t)
+{
+ void *ret = NULL;
+ int err = pthread_join((pthread_t)t, &ret);
+
+ if (err == 0) return ret;
+ return NULL;
+}
+
+static inline Eina_Bool
+_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
+{
+ int err;
+ pthread_attr_t attr;
+ sigset_t oldset, newset;
+
+ if (pthread_attr_init(&attr) != 0)
+ {
+ return EINA_FALSE;
+ }
+ if (affinity >= 0)
+ {
+#ifdef EINA_HAVE_PTHREAD_AFFINITY
+ cpu_set_t cpu;
+
+ CPU_ZERO(&cpu);
+ CPU_SET(affinity, &cpu);
+ pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
+#endif
+ }
+
+ /* setup initial locks */
+ sigemptyset(&newset);
+ sigaddset(&newset, SIGPIPE);
+ sigaddset(&newset, SIGALRM);
+ sigaddset(&newset, SIGCHLD);
+ sigaddset(&newset, SIGUSR1);
+ sigaddset(&newset, SIGUSR2);
+ sigaddset(&newset, SIGHUP);
+ sigaddset(&newset, SIGQUIT);
+ sigaddset(&newset, SIGINT);
+ sigaddset(&newset, SIGTERM);
+# ifdef SIGPWR
+ sigaddset(&newset, SIGPWR);
+# endif
+ pthread_sigmask(SIG_BLOCK, &newset, &oldset);
+ err = pthread_create((pthread_t *)t, &attr, func, data);
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ pthread_attr_destroy(&attr);
+
+ if (err == 0) return EINA_TRUE;
+
+ return EINA_FALSE;
+}
+
+static inline Eina_Bool
+_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
+{
+ return pthread_equal((pthread_t)t1, (pthread_t)t2);
+}
+
+static inline Eina_Thread
+_eina_thread_self(void)
+{
+ return (Eina_Thread)pthread_self();
+}
+
+
+typedef struct _Eina_Thread_Call Eina_Thread_Call;
+struct _Eina_Thread_Call
+{
+ Eina_Thread_Cb func;
+ const void *data;
+
+ Eina_Thread_Priority prio;
+ int affinity;
+};
+
+static void *
+_eina_internal_call(void *context)
+{
+ Eina_Thread_Call *c = context;
+ void *r;
+ pthread_t self;
+
+ // Default this thread to not cancellable as per Eina documentation
+ eina_thread_cancellable_set(EINA_FALSE, NULL);
+
+ EINA_THREAD_CLEANUP_PUSH(free, c);
+
+ self = pthread_self();
+
+ if (c->prio == EINA_THREAD_IDLE)
+ {
+ struct sched_param params;
+ int min;
+#ifdef SCHED_IDLE
+ int pol = SCHED_IDLE;
+#else
+ int pol;
+ pthread_getschedparam(self, &pol, &params);
+#endif
+ min = sched_get_priority_min(pol);
+ params.sched_priority = min;
+ pthread_setschedparam(self, pol, &params);
+ }
+ else if (c->prio == EINA_THREAD_BACKGROUND)
+ {
+ struct sched_param params;
+ int min, max;
+#ifdef SCHED_BATCH
+ int pol = SCHED_BATCH;
+#else
+ int pol;
+ pthread_getschedparam(self, &pol, &params);
+#endif
+ min = sched_get_priority_min(pol);
+ max = sched_get_priority_max(pol);
+ params.sched_priority = (max - min) / 2;
+ pthread_setschedparam(self, pol, &params);
+ }
+// do nothing for normal
+// else if (c->prio == EINA_THREAD_NORMAL)
+// {
+// }
+ else if (c->prio == EINA_THREAD_URGENT)
+ {
+ struct sched_param params;
+ int max, pol;
+
+ pthread_getschedparam(self, &pol, &params);
+ max = sched_get_priority_max(pol);
+ params.sched_priority += 5;
+ if (params.sched_priority > max) params.sched_priority = max;
+ pthread_setschedparam(self, pol, &params);
+ }
+
+ _eina_debug_thread_add(&self);
+ EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
+ r = c->func((void*) c->data, eina_thread_self());
+ EINA_THREAD_CLEANUP_POP(EINA_TRUE);
+ EINA_THREAD_CLEANUP_POP(EINA_TRUE);
+
+ return r;
+}
+
+EINA_API Eina_Thread
+eina_thread_self(void)
+{
+ return _eina_thread_self();
+}
+
+EINA_API Eina_Bool
+eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
+{
+ return !!_eina_thread_equal(t1, t2);
+}
+
+EINA_API Eina_Bool
+eina_thread_create(Eina_Thread *t,
+ Eina_Thread_Priority prio, int affinity,
+ Eina_Thread_Cb func, const void *data)
+{
+ Eina_Thread_Call *c;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
+
+ c = malloc(sizeof (Eina_Thread_Call));
+ if (!c) return EINA_FALSE;
+
+ c->func = func;
+ c->data = data;
+ c->prio = prio;
+ c->affinity = affinity;
+
+ // valgrind complains c is lost - but it's not - it is handed to the
+ // child thread to be freed when c->func returns in _eina_internal_call().
+ if (_eina_thread_create(t, affinity, _eina_internal_call, c))
+ return EINA_TRUE;
+
+ free(c);
+ return EINA_FALSE;
+}
+
+EINA_API void *
+eina_thread_join(Eina_Thread t)
+{
+ return _eina_thread_join(t);
+}
+
+EINA_API Eina_Bool
+eina_thread_name_set(Eina_Thread t, const char *name)
+{
+#ifdef EINA_HAVE_PTHREAD_SETNAME
+ char buf[16];
+ if (name)
+ {
+ strncpy(buf, name, 15);
+ buf[15] = 0;
+ }
+ else buf[0] = 0;
+#ifndef __linux__
+ pthread_set_name_np((pthread_t)t, buf);
+ return EINA_TRUE;
+#else
+ if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE;
+#endif
+#else
+ (void)t;
+ (void)name;
+#endif
+ return EINA_FALSE;
+}
+
+EINA_API Eina_Bool
+eina_thread_cancel(Eina_Thread t)
+{
+ if (!t) return EINA_FALSE;
+ return pthread_cancel((pthread_t)t) == 0;
+}
+
+EINA_API Eina_Bool
+eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
+{
+ int state = cancellable ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
+ int old = 0;
+ int r;
+
+ /* enforce deferred in case users changed to asynchronous themselves */
+ pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
+
+ r = pthread_setcancelstate(state, &old);
+ if (was_cancellable && r == 0)
+ *was_cancellable = (old == PTHREAD_CANCEL_ENABLE);
+
+ return r == 0;
+}
+
+EINA_API void
+eina_thread_cancel_checkpoint(void)
+{
+ pthread_testcancel();
+}
+
+EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
+
+EINA_API void
+eina_sched_prio_drop(void)
+{
+ struct sched_param param;
+ int pol, ret;
+ pthread_t pthread_id;
+
+ pthread_id = pthread_self();
+ ret = pthread_getschedparam(pthread_id, &pol, &param);
+ if (ret)
+ {
+ EINA_LOG_ERR("Unable to query sched parameters");
+ return;
+ }
+
+ if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO))
+ {
+ param.sched_priority -= RTNICENESS;
+
+ /* We don't change the policy */
+ if (param.sched_priority < 1)
+ {
+ EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
+ param.sched_priority = 1;
+ }
+
+ pthread_setschedparam(pthread_id, pol, &param);
+ }
+# ifdef __linux__
+ else
+ {
+ int prio;
+ errno = 0;
+ prio = getpriority(PRIO_PROCESS, 0);
+ if (errno == 0)
+ {
+ prio += NICENESS;
+ if (prio > 19)
+ {
+ EINA_LOG_INFO("Max niceness reached; keeping max (19)");
+ prio = 19;
+ }
+
+ setpriority(PRIO_PROCESS, 0, prio);
+ }
+ }
+# endif
+}
+
+EINA_API Eina_Bool
+eina_thread_init(void)
+{
+ return EINA_TRUE;
+}
+
+EINA_API Eina_Bool
+eina_thread_shutdown(void)
+{
+ return EINA_TRUE;
+}
diff --git a/src/lib/eina/eina_thread_win32.c b/src/lib/eina/eina_thread_win32.c
new file mode 100644
index 0000000000..8f5bf3c1ff
--- /dev/null
+++ b/src/lib/eina/eina_thread_win32.c
@@ -0,0 +1,397 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2020 Expertise Solutions Cons em Inf
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_types.h"
+#include "eina_config.h"
+#include "eina_array.h"
+#include "eina_thread.h"
+#include "eina_main.h"
+#include "eina_debug_private.h"
+#include "eina_log.h"
+
+#include <assert.h>
+#include <evil_private.h>
+#include <process.h>
+
+#define RTNICENESS 1
+#define NICENESS 5
+
+/*
+ * The underlying type of Eina_Thread
+ */
+struct Thread
+{
+ CRITICAL_SECTION cancel_lock; /* mutex to protect the cancel handle */
+ char name[16]; /* the thread name */
+ HANDLE handle; /* thread handle */
+ void *data; /* on entry, the thread function argument, on exit, the return value */
+ Eina_Thread_Cb fn; /* the thread function */
+ Eina_Array *cleanup_fn;
+ Eina_Array *cleanup_arg;
+ unsigned id; /* thread id */
+ Eina_Bool free_on_exit; /* free the structure when thread exit */
+ volatile Eina_Bool cancel; /* the cancel event handle */
+ volatile Eina_Bool cancellable; /* is cancel enabled? */
+};
+
+typedef struct Thread Thread_t;
+
+/*
+ * This TLS stores the Eina_Thread for the current thread
+ */
+static DWORD tls_thread_self = 0;
+
+static Thread_t main_thread = { 0 };
+
+/*
+ * If we alloc'ed the Thread_t in eina_thread_self, we set
+ * free_on_exit flag to true, we then free it here
+ */
+void
+free_thread(void)
+{
+ Thread_t *t = TlsGetValue(tls_thread_self);
+ if (t && t->free_on_exit)
+ {
+ if (t) eina_array_free(t->cleanup_fn);
+ if (t) eina_array_free(t->cleanup_arg);
+ free(t);
+ }
+}
+
+static unsigned
+thread_fn(void *arg)
+{
+ Thread_t *thr = arg;
+ TlsSetValue(tls_thread_self, thr);
+ _eina_debug_thread_add(&thr);
+ EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &thr);
+ thr->data = thr->fn(thr->data, (Eina_Thread) thr);
+ EINA_THREAD_CLEANUP_POP(EINA_TRUE);
+ return 0;
+}
+
+EINA_API Eina_Thread
+eina_thread_self(void)
+{
+ Thread_t *self = TlsGetValue(tls_thread_self);
+ /*
+ * If self is NULL this means
+ * 1) This function was called before eina_thread_init
+ * 2) This thread wasn't created by eina_thread_create
+ *
+ * In either case we alloc a new Thread struct and return
+ * it.
+ */
+ if (!self)
+ {
+ self = calloc(1, sizeof(*self));
+ self->handle = GetCurrentThread();
+ self->id = GetCurrentThreadId();
+ self->free_on_exit = EINA_TRUE;
+ self->cleanup_fn = eina_array_new(4);
+ self->cleanup_arg = eina_array_new(4);
+ if (tls_thread_self)
+ TlsSetValue(tls_thread_self, self);
+ }
+ return (Eina_Thread) self;
+}
+
+EINA_API Eina_Bool
+eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
+{
+ return ((Thread_t *) t1)->id == ((Thread_t *) t2)->id;
+}
+
+EINA_API Eina_Bool
+eina_thread_create(Eina_Thread *t, Eina_Thread_Priority prio,
+ int affinity, Eina_Thread_Cb func, const void *data)
+{
+ Thread_t *thr = calloc(1, sizeof(Thread_t));
+ if (!thr)
+ return EINA_FALSE;
+
+ thr->data = (void *) data;
+ thr->fn = func;
+
+ thr->handle = (HANDLE) _beginthreadex(NULL, 0, thread_fn, thr, CREATE_SUSPENDED, &thr->id);
+ if (!thr->handle)
+ goto fail;
+
+ int priority;
+ switch (prio)
+ {
+ case EINA_THREAD_URGENT:
+ priority = THREAD_PRIORITY_HIGHEST;
+ break;
+ case EINA_THREAD_BACKGROUND:
+ priority = THREAD_PRIORITY_BELOW_NORMAL;
+ break;
+ case EINA_THREAD_IDLE:
+ priority = THREAD_PRIORITY_IDLE;
+ break;
+ default:
+ priority = THREAD_PRIORITY_NORMAL;
+ }
+
+ if (!SetThreadPriority(thr->handle, priority))
+ goto fail;
+
+ if ((affinity >= 0) && (!SetThreadAffinityMask(thr->handle, 1 << affinity)))
+ goto fail;
+
+ thr->id = GetThreadId(thr->handle);
+ if (!thr->id)
+ goto fail;
+
+ thr->cleanup_fn = eina_array_new(4);
+ thr->cleanup_arg = eina_array_new(4);
+ if ((!thr->cleanup_fn) || (!thr->cleanup_arg))
+ goto fail;
+
+ InitializeCriticalSection(&thr->cancel_lock);
+
+ if (!ResumeThread(thr->handle))
+ goto cs_fail;
+
+ GetModuleFileNameA(NULL, thr->name, sizeof(thr->name));
+ *t = (Eina_Thread) thr;
+ return EINA_TRUE;
+
+cs_fail:
+ DeleteCriticalSection(&thr->cancel_lock);
+fail:
+ if (thr)
+ {
+ if (thr->handle) CloseHandle(thr->handle);
+ if (thr->cleanup_fn) eina_array_free(thr->cleanup_fn);
+ if (thr->cleanup_arg) eina_array_free(thr->cleanup_arg);
+ free(thr);
+ }
+ return EINA_FALSE;
+}
+
+EINA_API void *
+eina_thread_join(Eina_Thread t)
+{
+ void *data;
+ Thread_t *thr = (Thread_t *) t;
+
+ if (WAIT_OBJECT_0 == WaitForSingleObject(thr->handle, INFINITE))
+ data = thr->data;
+ else
+ data = NULL;
+
+ DeleteCriticalSection(&thr->cancel_lock);
+ CloseHandle(thr->handle);
+ eina_array_free(thr->cleanup_fn);
+ eina_array_free(thr->cleanup_arg);
+ free(thr);
+
+ return data;
+}
+
+EINA_API Eina_Bool
+eina_thread_name_set(Eina_Thread t, const char *name)
+{
+ Thread_t *thr = (Thread_t *) t;
+ strncpy(thr->name, name, sizeof(thr->name));
+ thr->name[sizeof(thr->name)-1] = '\0';
+ return EINA_TRUE;
+}
+
+EINA_API Eina_Bool
+eina_thread_cancel(Eina_Thread t)
+{
+ Eina_Bool ret = EINA_FALSE;
+ Thread_t *thr = (Thread_t *) t;
+
+ if (thr)
+ {
+ EnterCriticalSection(&thr->cancel_lock);
+ if (thr->cancellable)
+ {
+ thr->cancel = EINA_TRUE;
+ ret = EINA_TRUE;
+ }
+ LeaveCriticalSection(&thr->cancel_lock);
+ }
+ return ret;
+}
+
+EINA_API Eina_Bool
+eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
+{
+ Thread_t *t = (Thread_t *) eina_thread_self();
+
+ EnterCriticalSection(&t->cancel_lock);
+ if (was_cancellable) *was_cancellable = t->cancellable;
+ t->cancellable = cancellable;
+ LeaveCriticalSection(&t->cancel_lock);
+
+ return EINA_TRUE;
+}
+
+EINA_API void
+eina_thread_cancel_checkpoint(void)
+{
+ Eina_Bool cancel;
+ Thread_t *t = (Thread_t *) eina_thread_self();
+
+ EnterCriticalSection(&t->cancel_lock);
+ cancel = t->cancellable && t->cancel;
+ LeaveCriticalSection(&t->cancel_lock);
+
+ if (cancel)
+ {
+ t->data = (void *) EINA_THREAD_JOIN_CANCELED;
+ while (eina_array_count(t->cleanup_fn))
+ {
+ Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn);
+ void *arg = eina_array_pop(t->cleanup_arg);
+
+ if (fn)
+ fn(arg);
+ }
+
+ ExitThread(0);
+ }
+}
+
+EINA_API Eina_Bool
+eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data)
+{
+ Thread_t *t = TlsGetValue(tls_thread_self);
+ assert(t);
+
+ if (!eina_array_push(t->cleanup_fn, fn))
+ return EINA_FALSE;
+
+ if (!eina_array_push(t->cleanup_arg, data))
+ {
+ eina_array_pop(t->cleanup_fn);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+EINA_API void
+eina_thread_cleanup_pop(int execute)
+{
+ Thread_t *t = TlsGetValue(tls_thread_self);
+ assert(t);
+
+ if (eina_array_count(t->cleanup_fn))
+ {
+ Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn);
+ void *arg = eina_array_pop(t->cleanup_arg);
+
+ if (execute && fn)
+ fn(arg);
+ }
+}
+
+EINA_API const void *EINA_THREAD_JOIN_CANCELED = (void *) -1L;
+
+void
+eina_sched_prio_drop(void)
+{
+ Thread_t *thread;
+ int sched_priority;
+
+ thread = (Thread_t *) eina_thread_self();
+
+ sched_priority = GetThreadPriority(thread->handle);
+
+ if (EINA_UNLIKELY(sched_priority == THREAD_PRIORITY_TIME_CRITICAL))
+ {
+ sched_priority -= RTNICENESS;
+
+ /* We don't change the policy */
+ if (sched_priority < 1)
+ {
+ EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
+ sched_priority = 1;
+ }
+ if (!SetThreadPriority(thread->handle, sched_priority))
+ {
+ EINA_LOG_ERR("Unable to query sched parameters");
+ }
+ }
+ else
+ {
+ sched_priority += NICENESS;
+
+ /* We don't change the policy */
+ if (sched_priority > THREAD_PRIORITY_TIME_CRITICAL)
+ {
+ EINA_LOG_INFO("Max niceness reached; keeping max (THREAD_PRIORITY_TIME_CRITICAL)");
+ sched_priority = THREAD_PRIORITY_TIME_CRITICAL;
+ }
+ if (!SetThreadPriority(thread->handle, sched_priority))
+ {
+ EINA_LOG_ERR("Unable to query sched parameters");
+ }
+ }
+}
+
+EINA_API Eina_Bool
+eina_thread_init(void)
+{
+ if (!eina_main_loop_is())
+ return EINA_FALSE;
+
+ tls_thread_self = TlsAlloc();
+ if (TLS_OUT_OF_INDEXES == tls_thread_self)
+ return EINA_FALSE;
+
+ if (!TlsSetValue(tls_thread_self, &main_thread))
+ {
+ assert(0);
+ TlsFree(tls_thread_self);
+ return EINA_FALSE;
+ }
+
+ main_thread.cancellable = EINA_FALSE;
+ main_thread.cancel = EINA_FALSE;
+ main_thread.handle = GetCurrentThread();
+ main_thread.id = GetCurrentThreadId();
+
+ InitializeCriticalSection(&main_thread.cancel_lock);
+ main_thread.cleanup_fn = eina_array_new(2);
+ main_thread.cleanup_arg = eina_array_new(2);
+
+ GetModuleFileNameA(NULL, main_thread.name, sizeof(main_thread.name)/sizeof(main_thread.name[0]));
+
+ return EINA_TRUE;
+}
+
+EINA_API Eina_Bool
+eina_thread_shutdown(void)
+{
+ DeleteCriticalSection(&main_thread.cancel_lock);
+ eina_array_free(main_thread.cleanup_fn);
+ eina_array_free(main_thread.cleanup_arg);
+ TlsFree(tls_thread_self);
+ return EINA_TRUE;
+}
diff --git a/src/lib/eina/eina_win32_dllmain.c b/src/lib/eina/eina_win32_dllmain.c
new file mode 100644
index 0000000000..3285aebebb
--- /dev/null
+++ b/src/lib/eina/eina_win32_dllmain.c
@@ -0,0 +1,18 @@
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "eina_config.h"
+#include "eina_types.h"
+#include <evil_private.h>
+
+void free_thread(void);
+
+BOOL WINAPI
+DllMain(HINSTANCE inst EINA_UNUSED, WORD reason, PVOID reserved EINA_UNUSED)
+{
+ if (DLL_THREAD_DETACH == reason)
+ free_thread();
+
+ return TRUE;
+}
diff --git a/src/lib/eina/meson.build b/src/lib/eina/meson.build
index 45d0781680..21d35493e5 100644
--- a/src/lib/eina/meson.build
+++ b/src/lib/eina/meson.build
@@ -66,7 +66,6 @@ public_sub_headers = [
'eina_main.h',
'eina_cpu.h',
'eina_inline_cpu.x',
-'eina_sched.h',
'eina_tiler.h',
'eina_hamster.h',
'eina_matrixsparse.h',
@@ -171,7 +170,6 @@ eina_src = files([
'eina_rbtree.c',
'eina_rectangle.c',
'eina_safety_checks.c',
-'eina_sched.c',
'eina_share_common.c',
'eina_simple_xml_parser.c',
'eina_str.c',
@@ -203,10 +201,10 @@ eina_src = files([
]) + eina_mp_sources
if sys_windows == true
- eina_src += files('eina_file_win32.c')
+ eina_src += files('eina_file_win32.c', 'eina_win32_dllmain.c', 'eina_thread_win32.c')
eina_src += 'eina_fnmatch.c'
else
- eina_src += files('eina_file.c')
+ eina_src += files('eina_file.c', 'eina_thread_posix.c')
endif
eina_config = configuration_data()
diff --git a/src/tests/eina/eina_suite.c b/src/tests/eina/eina_suite.c
index 01ed82a532..7561878c8b 100644
--- a/src/tests/eina/eina_suite.c
+++ b/src/tests/eina/eina_suite.c
@@ -91,6 +91,7 @@ static const Efl_Test_Case etc[] = {
{ "Vpath", eina_test_vpath },
{ "debug", eina_test_debug },
{ "Abstract Content", eina_test_abstract_content },
+ { "thread", eina_test_thread },
{ NULL, NULL }
};
diff --git a/src/tests/eina/eina_suite.h b/src/tests/eina/eina_suite.h
index 84d6e60516..c9e5476ae7 100644
--- a/src/tests/eina/eina_suite.h
+++ b/src/tests/eina/eina_suite.h
@@ -79,5 +79,6 @@ void eina_test_slstr(TCase *tc);
void eina_test_vpath(TCase *tc);
void eina_test_debug(TCase *tc);
void eina_test_abstract_content(TCase *tc);
+void eina_test_thread(TCase *tc);
#endif /* EINA_SUITE_H_ */
diff --git a/src/tests/eina/eina_test_thread.c b/src/tests/eina/eina_test_thread.c
new file mode 100644
index 0000000000..2b35907db7
--- /dev/null
+++ b/src/tests/eina/eina_test_thread.c
@@ -0,0 +1,124 @@
+/* EINA - EFL data type library
+ * Copyright (C) 2020 Expertise Solutions Cons em Inf
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library;
+ * if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <check.h>
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef HAVE_UNISTD
+# include <unistd.h>
+#endif
+
+#ifdef _WIN32
+# include <evil_private.h> /* mkdir */
+#endif
+
+#include <Eina.h>
+#include "eina_suite.h"
+
+static void
+thread_cleanup_fn(void *arg)
+{
+ *(int *) arg = 1;
+}
+
+static void *
+thread_fn_execute(void *arg, Eina_Thread t EINA_UNUSED)
+{
+ EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
+ EINA_THREAD_CLEANUP_POP(1);
+ return NULL;
+}
+
+static void *
+thread_fn_skip(void *arg, Eina_Thread t EINA_UNUSED)
+{
+ EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
+ EINA_THREAD_CLEANUP_POP(0);
+ return NULL;
+}
+
+static void *
+thread_fn_cancel(void *arg, Eina_Thread t EINA_UNUSED)
+{
+ Eina_Condition *cond = arg;
+
+ ck_assert(eina_thread_cancellable_set(EINA_TRUE, NULL));
+ ck_assert(eina_condition_signal(cond));
+
+ for (size_t i = 0; i < 100; ++i)
+ {
+ eina_thread_cancel_checkpoint();
+#ifdef _WIN32
+ Sleep(100);
+#else
+ usleep(100 * 1000);
+#endif
+ }
+
+ return NULL;
+}
+
+EFL_START_TEST(eina_thread_test_cleanup_execute)
+{
+ Eina_Thread t;
+ int flag = 0;
+ ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_execute, &flag));
+ eina_thread_join(t);
+ ck_assert_uint_eq(flag, 1);
+}
+EFL_END_TEST
+
+EFL_START_TEST(eina_thread_test_cleanup_skip)
+{
+ Eina_Thread t;
+ int flag = 2;
+ ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_skip, &flag));
+ eina_thread_join(t);
+ ck_assert_uint_eq(flag, 2);
+}
+EFL_END_TEST
+
+EFL_START_TEST(eina_thread_test_cancel)
+{
+ Eina_Thread t;
+ Eina_Lock mutex;
+ Eina_Condition cond;
+
+ ck_assert(eina_lock_new(&mutex));
+ ck_assert(eina_condition_new(&cond, &mutex));
+
+ ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_cancel, &cond));
+ ck_assert(eina_lock_take(&mutex));
+ ck_assert(eina_condition_wait(&cond));
+ ck_assert(eina_thread_cancel(t));
+ ck_assert_ptr_eq(eina_thread_join(t), EINA_THREAD_JOIN_CANCELED);
+
+ eina_condition_free(&cond);
+ eina_lock_free(&mutex);
+}
+EFL_END_TEST
+
+void
+eina_test_thread(TCase *tc)
+{
+ tcase_add_test(tc, eina_thread_test_cleanup_skip);
+ tcase_add_test(tc, eina_thread_test_cleanup_execute);
+ tcase_add_test(tc, eina_thread_test_cancel);
+}
diff --git a/src/tests/eina/meson.build b/src/tests/eina/meson.build
index 18c8f91ced..0d09819e74 100644
--- a/src/tests/eina/meson.build
+++ b/src/tests/eina/meson.build
@@ -56,6 +56,7 @@ eina_test_src = files(
'eina_test_slstr.c',
'eina_test_vpath.c',
'eina_test_abstract_content.c',
+'eina_test_thread.c',
)