summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWander Lairson Costa <wander.lairson@gmail.com>2021-04-17 15:31:09 -0300
committerFelipe Magno de Almeida <felipe@expertise.dev>2021-04-17 16:00:32 -0300
commit65d528a3798b72dbd2e01b4f37caa898af50e359 (patch)
tree74c68eead99185fa684d23326e6b71dc47ac4688 /src
parent287834b0dafa2c96c8920c792348b48655f06766 (diff)
Implement eina_thread for native windows
Summary: 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, vtorri, cedric, walac Reviewed By: jptiz, cedric Subscribers: raster, cedric, #reviewers, #committers, lucas Tags: #efl Differential Revision: https://phab.enlightenment.org/D12037
Diffstat (limited to 'src')
-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
13 files changed, 954 insertions, 431 deletions
diff --git a/src/lib/eina/Eina.h b/src/lib/eina/Eina.h
index 383696d7d8..d2ce89e805 100644
--- a/src/lib/eina/Eina.h
+++ b/src/lib/eina/Eina.h
@@ -241,7 +241,6 @@ extern "C" {
241#include <eina_benchmark.h> 241#include <eina_benchmark.h>
242#include <eina_convert.h> 242#include <eina_convert.h>
243#include <eina_cpu.h> 243#include <eina_cpu.h>
244#include <eina_sched.h>
245#include <eina_tiler.h> 244#include <eina_tiler.h>
246#include <eina_hamster.h> 245#include <eina_hamster.h>
247#include <eina_matrixsparse.h> 246#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 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2010 ProFUSION embedded systems
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <pthread.h>
24#ifdef __linux__
25# include <sched.h>
26# include <sys/time.h>
27# include <sys/resource.h>
28# include <errno.h>
29#endif
30
31#include "eina_sched.h"
32#include "eina_log.h"
33
34#define RTNICENESS 1
35#define NICENESS 5
36
37EINA_API void
38eina_sched_prio_drop(void)
39{
40 struct sched_param param;
41 int pol, ret;
42 pthread_t pthread_id;
43
44 pthread_id = pthread_self();
45 ret = pthread_getschedparam(pthread_id, &pol, &param);
46 if (ret)
47 {
48 EINA_LOG_ERR("Unable to query sched parameters");
49 return;
50 }
51
52 if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO))
53 {
54 param.sched_priority -= RTNICENESS;
55
56 /* We don't change the policy */
57 if (param.sched_priority < 1)
58 {
59 EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
60 param.sched_priority = 1;
61 }
62
63 pthread_setschedparam(pthread_id, pol, &param);
64 }
65# ifdef __linux__
66 else
67 {
68 int prio;
69 errno = 0;
70 prio = getpriority(PRIO_PROCESS, 0);
71 if (errno == 0)
72 {
73 prio += NICENESS;
74 if (prio > 19)
75 {
76 EINA_LOG_INFO("Max niceness reached; keeping max (19)");
77 prio = 19;
78 }
79
80 setpriority(PRIO_PROCESS, 0, prio);
81 }
82 }
83# endif
84}
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 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2010 ProFUSION embedded systems
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19/**
20 * @defgroup Schedule Schedule
21 * @ingroup Eina_Tools_Group
22 *
23 * @{
24 *
25 * TODO: description
26 *
27 */
28
29#ifndef EINA_SCHED_H_
30#define EINA_SCHED_H_
31
32#include "eina_types.h"
33
34
35/**
36 * @brief Lowers the priority of the current thread.
37 *
38 * @details It's used by worker threads so that they use up the background CPU and do not stall
39 * the main thread. If the current thread is running with real-time priority, we
40 * decrease our priority by @c RTNICENESS. This is done in a portable way.
41 *
42 * Otherwise, (we are running with the SCHED_OTHER policy) there's no portable way to
43 * set the nice level on the current thread. In Linux, it does work and it's the
44 * only one that is implemented as of now. In this case, the nice level is
45 * incremented on this thread by @c NICENESS.
46 */
47EINA_API void eina_sched_prio_drop(void);
48
49/**
50 * @}
51 */
52
53#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 @@
20# include "config.h" 20# include "config.h"
21#endif 21#endif
22 22
23#include <stdlib.h> 23#include <stddef.h>
24 24
25#include "eina_config.h" 25#ifdef HAVE_PTHREAD_H
26#include "eina_lock.h" /* it will include pthread.h with proper flags */ 26# include <pthread.h>
27#include "eina_thread.h"
28#include "eina_sched.h"
29#include "eina_cpu.h"
30
31/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
32#include "eina_safety_checks.h"
33
34#include "eina_debug_private.h"
35
36#include <pthread.h>
37#include <errno.h>
38#ifndef _WIN32
39# include <signal.h>
40#endif
41# include <string.h>
42
43#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
44#ifndef __linux__
45#include <pthread_np.h>
46#define cpu_set_t cpuset_t
47#endif
48#endif
49
50static inline void *
51_eina_thread_join(Eina_Thread t)
52{
53 void *ret = NULL;
54 int err = pthread_join((pthread_t)t, &ret);
55
56 if (err == 0) return ret;
57 return NULL;
58}
59
60static inline Eina_Bool
61_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
62{
63 int err;
64 pthread_attr_t attr;
65#ifndef _WIN32
66 sigset_t oldset, newset;
67#endif
68
69 if (pthread_attr_init(&attr) != 0)
70 {
71 return EINA_FALSE;
72 }
73 if (affinity >= 0)
74 {
75#ifdef EINA_HAVE_PTHREAD_AFFINITY
76 cpu_set_t cpu;
77
78 CPU_ZERO(&cpu);
79 CPU_SET(affinity, &cpu);
80 pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
81#endif
82 }
83
84 /* setup initial locks */
85#ifndef _WIN32
86 sigemptyset(&newset);
87 sigaddset(&newset, SIGPIPE);
88 sigaddset(&newset, SIGALRM);
89 sigaddset(&newset, SIGCHLD);
90 sigaddset(&newset, SIGUSR1);
91 sigaddset(&newset, SIGUSR2);
92 sigaddset(&newset, SIGHUP);
93 sigaddset(&newset, SIGQUIT);
94 sigaddset(&newset, SIGINT);
95 sigaddset(&newset, SIGTERM);
96# ifdef SIGPWR
97 sigaddset(&newset, SIGPWR);
98# endif
99 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
100#endif
101 err = pthread_create((pthread_t *)t, &attr, func, data);
102#ifndef _WIN32
103 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
104#endif
105 pthread_attr_destroy(&attr);
106
107 if (err == 0) return EINA_TRUE;
108
109 return EINA_FALSE;
110}
111
112static inline Eina_Bool
113_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
114{
115 return pthread_equal((pthread_t)t1, (pthread_t)t2);
116}
117
118static inline Eina_Thread
119_eina_thread_self(void)
120{
121 return (Eina_Thread)pthread_self();
122}
123
124
125typedef struct _Eina_Thread_Call Eina_Thread_Call;
126struct _Eina_Thread_Call
127{
128 Eina_Thread_Cb func;
129 const void *data;
130
131 Eina_Thread_Priority prio;
132 int affinity;
133};
134
135static void *
136_eina_internal_call(void *context)
137{
138 Eina_Thread_Call *c = context;
139 void *r;
140 pthread_t self;
141
142 // Default this thread to not cancellable as per Eina documentation
143 eina_thread_cancellable_set(EINA_FALSE, NULL);
144
145 EINA_THREAD_CLEANUP_PUSH(free, c);
146
147 self = pthread_self();
148
149 if (c->prio == EINA_THREAD_IDLE)
150 {
151 struct sched_param params;
152 int min;
153#ifdef SCHED_IDLE
154 int pol = SCHED_IDLE;
155#else
156 int pol;
157 pthread_getschedparam(self, &pol, &params);
158#endif 27#endif
159 min = sched_get_priority_min(pol);
160 params.sched_priority = min;
161 pthread_setschedparam(self, pol, &params);
162 }
163 else if (c->prio == EINA_THREAD_BACKGROUND)
164 {
165 struct sched_param params;
166 int min, max;
167#ifdef SCHED_BATCH
168 int pol = SCHED_BATCH;
169#else
170 int pol;
171 pthread_getschedparam(self, &pol, &params);
172#endif
173 min = sched_get_priority_min(pol);
174 max = sched_get_priority_max(pol);
175 params.sched_priority = (max - min) / 2;
176 pthread_setschedparam(self, pol, &params);
177 }
178// do nothing for normal
179// else if (c->prio == EINA_THREAD_NORMAL)
180// {
181// }
182 else if (c->prio == EINA_THREAD_URGENT)
183 {
184 struct sched_param params;
185 int max, pol;
186
187 pthread_getschedparam(self, &pol, &params);
188 max = sched_get_priority_max(pol);
189 params.sched_priority += 5;
190 if (params.sched_priority > max) params.sched_priority = max;
191 pthread_setschedparam(self, pol, &params);
192 }
193
194 _eina_debug_thread_add(&self);
195 EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
196 r = c->func((void*) c->data, eina_thread_self());
197 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
198 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
199 28
200 return r; 29#include "eina_types.h"
201} 30#include "eina_config.h"
202 31#include "eina_thread.h"
203EINA_API Eina_Thread
204eina_thread_self(void)
205{
206 return _eina_thread_self();
207}
208
209EINA_API Eina_Bool
210eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
211{
212 return !!_eina_thread_equal(t1, t2);
213}
214
215EINA_API Eina_Bool
216eina_thread_create(Eina_Thread *t,
217 Eina_Thread_Priority prio, int affinity,
218 Eina_Thread_Cb func, const void *data)
219{
220 Eina_Thread_Call *c;
221
222 EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
223 EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
224
225 c = malloc(sizeof (Eina_Thread_Call));
226 if (!c) return EINA_FALSE;
227
228 c->func = func;
229 c->data = data;
230 c->prio = prio;
231 c->affinity = affinity;
232
233 // valgrind complains c is lost - but it's not - it is handed to the
234 // child thread to be freed when c->func returns in _eina_internal_call().
235 if (_eina_thread_create(t, affinity, _eina_internal_call, c))
236 return EINA_TRUE;
237
238 free(c);
239 return EINA_FALSE;
240}
241
242EINA_API void *
243eina_thread_join(Eina_Thread t)
244{
245 return _eina_thread_join(t);
246}
247
248EINA_API Eina_Bool
249eina_thread_name_set(Eina_Thread t, const char *name)
250{
251#ifdef EINA_HAVE_PTHREAD_SETNAME
252 char buf[16];
253 if (name)
254 {
255 strncpy(buf, name, 15);
256 buf[15] = 0;
257 }
258 else buf[0] = 0;
259#ifndef __linux__
260 pthread_set_name_np((pthread_t)t, buf);
261 return EINA_TRUE;
262#else
263 if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE;
264#endif
265#else
266 (void)t;
267 (void)name;
268#endif
269 return EINA_FALSE;
270}
271
272EINA_API Eina_Bool
273eina_thread_cancel(Eina_Thread t)
274{
275 if (!t) return EINA_FALSE;
276 return pthread_cancel((pthread_t)t) == 0;
277}
278
279EINA_API Eina_Bool
280eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
281{
282 int state = cancellable ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
283 int old = 0;
284 int r;
285
286 /* enforce deferred in case users changed to asynchronous themselves */
287 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
288
289 r = pthread_setcancelstate(state, &old);
290 if (was_cancellable && r == 0)
291 *was_cancellable = (old == PTHREAD_CANCEL_ENABLE);
292
293 return r == 0;
294}
295
296EINA_API void
297eina_thread_cancel_checkpoint(void)
298{
299 pthread_testcancel();
300}
301 32
302EINA_API void * 33EINA_API void *
303eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data) 34eina_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
312 eina_thread_cancellable_set(old, NULL); 43 eina_thread_cancellable_set(old, NULL);
313 return ret; 44 return ret;
314} 45}
315
316EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
317
318Eina_Bool
319eina_thread_init(void)
320{
321 return EINA_TRUE;
322}
323
324Eina_Bool
325eina_thread_shutdown(void)
326{
327 return EINA_TRUE;
328}
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;
52typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t); 52typedef void *(*Eina_Thread_Cb)(void *data, Eina_Thread t);
53 53
54/** 54/**
55 * @typedef Eina_Thread_Cleanup_Cb
56 * Type for the definition of a thread cleanup function
57 */
58typedef void (*Eina_Thread_Cleanup_Cb) (void *data);
59
60/**
55 * @typedef Eina_Thread_Priority 61 * @typedef Eina_Thread_Priority
56 * Type to enumerate different thread priorities 62 * Type to enumerate different thread priorities
57 */ 63 */
@@ -248,8 +254,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
248 * 254 *
249 * @since 1.19 255 * @since 1.19
250 */ 256 */
257#ifdef _WIN32
258EINA_API Eina_Bool
259eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data);
260
261#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
262 eina_thread_cleanup_push(cleanup, data)
263#else
251#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \ 264#define EINA_THREAD_CLEANUP_PUSH(cleanup, data) \
252 pthread_cleanup_push(cleanup, data) 265 pthread_cleanup_push(cleanup, data)
266#endif
253 267
254/** 268/**
255 * @def EINA_THREAD_CLEANUP_POP(exec_cleanup) 269 * @def EINA_THREAD_CLEANUP_POP(exec_cleanup)
@@ -278,8 +292,16 @@ EINA_API void eina_thread_cancel_checkpoint(void);
278 * 292 *
279 * @since 1.19 293 * @since 1.19
280 */ 294 */
295#ifdef _WIN32
296EINA_API void
297eina_thread_cleanup_pop(int execute);
298
299#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
300 eina_thread_cleanup_pop(exec_cleanup)
301#else
281#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \ 302#define EINA_THREAD_CLEANUP_POP(exec_cleanup) \
282 pthread_cleanup_pop(exec_cleanup) 303 pthread_cleanup_pop(exec_cleanup)
304#endif
283 305
284/** 306/**
285 * @typedef Eina_Thread_Cancellable_Run_Cb 307 * @typedef Eina_Thread_Cancellable_Run_Cb
@@ -334,6 +356,20 @@ typedef void *(*Eina_Thread_Cancellable_Run_Cb)(void *data);
334EINA_API void *eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data); 356EINA_API void *eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data);
335 357
336/** 358/**
359 * @brief Lowers the priority of the current thread.
360 *
361 * @details It's used by worker threads so that they use up the background CPU and do not stall
362 * the main thread. If the current thread is running with real-time priority, we
363 * decrease our priority by @c RTNICENESS. This is done in a portable way.
364 *
365 * Otherwise, (we are running with the SCHED_OTHER policy) there's no portable way to
366 * set the nice level on the current thread. In Linux, it does work and it's the
367 * only one that is implemented as of now. In this case, the nice level is
368 * incremented on this thread by @c NICENESS.
369 */
370EINA_API void eina_sched_prio_drop(void);
371
372/**
337 * @} 373 * @}
338 */ 374 */
339 375
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 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2012 Cedric Bail
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include <stdlib.h>
24
25#include "eina_config.h"
26#include "eina_lock.h" /* it will include pthread.h with proper flags */
27#include "eina_thread.h"
28#include "eina_cpu.h"
29
30/* undefs EINA_ARG_NONULL() so NULL checks are not compiled out! */
31#include "eina_safety_checks.h"
32
33#include "eina_debug_private.h"
34
35#include <pthread.h>
36#include <errno.h>
37#ifndef _WIN32
38# include <signal.h>
39#endif
40# include <string.h>
41
42#if defined(EINA_HAVE_PTHREAD_AFFINITY) || defined(EINA_HAVE_PTHREAD_SETNAME)
43#ifndef __linux__
44#include <pthread_np.h>
45#define cpu_set_t cpuset_t
46#endif
47#endif
48
49#ifdef __linux__
50# include <sched.h>
51# include <sys/time.h>
52# include <sys/resource.h>
53#endif
54
55#include "eina_log.h"
56
57#define RTNICENESS 1
58#define NICENESS 5
59
60
61static inline void *
62_eina_thread_join(Eina_Thread t)
63{
64 void *ret = NULL;
65 int err = pthread_join((pthread_t)t, &ret);
66
67 if (err == 0) return ret;
68 return NULL;
69}
70
71static inline Eina_Bool
72_eina_thread_create(Eina_Thread *t, int affinity, void *(*func)(void *data), void *data)
73{
74 int err;
75 pthread_attr_t attr;
76 sigset_t oldset, newset;
77
78 if (pthread_attr_init(&attr) != 0)
79 {
80 return EINA_FALSE;
81 }
82 if (affinity >= 0)
83 {
84#ifdef EINA_HAVE_PTHREAD_AFFINITY
85 cpu_set_t cpu;
86
87 CPU_ZERO(&cpu);
88 CPU_SET(affinity, &cpu);
89 pthread_attr_setaffinity_np(&attr, sizeof(cpu), &cpu);
90#endif
91 }
92
93 /* setup initial locks */
94 sigemptyset(&newset);
95 sigaddset(&newset, SIGPIPE);
96 sigaddset(&newset, SIGALRM);
97 sigaddset(&newset, SIGCHLD);
98 sigaddset(&newset, SIGUSR1);
99 sigaddset(&newset, SIGUSR2);
100 sigaddset(&newset, SIGHUP);
101 sigaddset(&newset, SIGQUIT);
102 sigaddset(&newset, SIGINT);
103 sigaddset(&newset, SIGTERM);
104# ifdef SIGPWR
105 sigaddset(&newset, SIGPWR);
106# endif
107 pthread_sigmask(SIG_BLOCK, &newset, &oldset);
108 err = pthread_create((pthread_t *)t, &attr, func, data);
109 pthread_sigmask(SIG_SETMASK, &oldset, NULL);
110 pthread_attr_destroy(&attr);
111
112 if (err == 0) return EINA_TRUE;
113
114 return EINA_FALSE;
115}
116
117static inline Eina_Bool
118_eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
119{
120 return pthread_equal((pthread_t)t1, (pthread_t)t2);
121}
122
123static inline Eina_Thread
124_eina_thread_self(void)
125{
126 return (Eina_Thread)pthread_self();
127}
128
129
130typedef struct _Eina_Thread_Call Eina_Thread_Call;
131struct _Eina_Thread_Call
132{
133 Eina_Thread_Cb func;
134 const void *data;
135
136 Eina_Thread_Priority prio;
137 int affinity;
138};
139
140static void *
141_eina_internal_call(void *context)
142{
143 Eina_Thread_Call *c = context;
144 void *r;
145 pthread_t self;
146
147 // Default this thread to not cancellable as per Eina documentation
148 eina_thread_cancellable_set(EINA_FALSE, NULL);
149
150 EINA_THREAD_CLEANUP_PUSH(free, c);
151
152 self = pthread_self();
153
154 if (c->prio == EINA_THREAD_IDLE)
155 {
156 struct sched_param params;
157 int min;
158#ifdef SCHED_IDLE
159 int pol = SCHED_IDLE;
160#else
161 int pol;
162 pthread_getschedparam(self, &pol, &params);
163#endif
164 min = sched_get_priority_min(pol);
165 params.sched_priority = min;
166 pthread_setschedparam(self, pol, &params);
167 }
168 else if (c->prio == EINA_THREAD_BACKGROUND)
169 {
170 struct sched_param params;
171 int min, max;
172#ifdef SCHED_BATCH
173 int pol = SCHED_BATCH;
174#else
175 int pol;
176 pthread_getschedparam(self, &pol, &params);
177#endif
178 min = sched_get_priority_min(pol);
179 max = sched_get_priority_max(pol);
180 params.sched_priority = (max - min) / 2;
181 pthread_setschedparam(self, pol, &params);
182 }
183// do nothing for normal
184// else if (c->prio == EINA_THREAD_NORMAL)
185// {
186// }
187 else if (c->prio == EINA_THREAD_URGENT)
188 {
189 struct sched_param params;
190 int max, pol;
191
192 pthread_getschedparam(self, &pol, &params);
193 max = sched_get_priority_max(pol);
194 params.sched_priority += 5;
195 if (params.sched_priority > max) params.sched_priority = max;
196 pthread_setschedparam(self, pol, &params);
197 }
198
199 _eina_debug_thread_add(&self);
200 EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &self);
201 r = c->func((void*) c->data, eina_thread_self());
202 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
203 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
204
205 return r;
206}
207
208EINA_API Eina_Thread
209eina_thread_self(void)
210{
211 return _eina_thread_self();
212}
213
214EINA_API Eina_Bool
215eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
216{
217 return !!_eina_thread_equal(t1, t2);
218}
219
220EINA_API Eina_Bool
221eina_thread_create(Eina_Thread *t,
222 Eina_Thread_Priority prio, int affinity,
223 Eina_Thread_Cb func, const void *data)
224{
225 Eina_Thread_Call *c;
226
227 EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
228 EINA_SAFETY_ON_NULL_RETURN_VAL(func, EINA_FALSE);
229
230 c = malloc(sizeof (Eina_Thread_Call));
231 if (!c) return EINA_FALSE;
232
233 c->func = func;
234 c->data = data;
235 c->prio = prio;
236 c->affinity = affinity;
237
238 // valgrind complains c is lost - but it's not - it is handed to the
239 // child thread to be freed when c->func returns in _eina_internal_call().
240 if (_eina_thread_create(t, affinity, _eina_internal_call, c))
241 return EINA_TRUE;
242
243 free(c);
244 return EINA_FALSE;
245}
246
247EINA_API void *
248eina_thread_join(Eina_Thread t)
249{
250 return _eina_thread_join(t);
251}
252
253EINA_API Eina_Bool
254eina_thread_name_set(Eina_Thread t, const char *name)
255{
256#ifdef EINA_HAVE_PTHREAD_SETNAME
257 char buf[16];
258 if (name)
259 {
260 strncpy(buf, name, 15);
261 buf[15] = 0;
262 }
263 else buf[0] = 0;
264#ifndef __linux__
265 pthread_set_name_np((pthread_t)t, buf);
266 return EINA_TRUE;
267#else
268 if (pthread_setname_np((pthread_t)t, buf) == 0) return EINA_TRUE;
269#endif
270#else
271 (void)t;
272 (void)name;
273#endif
274 return EINA_FALSE;
275}
276
277EINA_API Eina_Bool
278eina_thread_cancel(Eina_Thread t)
279{
280 if (!t) return EINA_FALSE;
281 return pthread_cancel((pthread_t)t) == 0;
282}
283
284EINA_API Eina_Bool
285eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
286{
287 int state = cancellable ? PTHREAD_CANCEL_ENABLE : PTHREAD_CANCEL_DISABLE;
288 int old = 0;
289 int r;
290
291 /* enforce deferred in case users changed to asynchronous themselves */
292 pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, &old);
293
294 r = pthread_setcancelstate(state, &old);
295 if (was_cancellable && r == 0)
296 *was_cancellable = (old == PTHREAD_CANCEL_ENABLE);
297
298 return r == 0;
299}
300
301EINA_API void
302eina_thread_cancel_checkpoint(void)
303{
304 pthread_testcancel();
305}
306
307EINA_API const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
308
309EINA_API void
310eina_sched_prio_drop(void)
311{
312 struct sched_param param;
313 int pol, ret;
314 pthread_t pthread_id;
315
316 pthread_id = pthread_self();
317 ret = pthread_getschedparam(pthread_id, &pol, &param);
318 if (ret)
319 {
320 EINA_LOG_ERR("Unable to query sched parameters");
321 return;
322 }
323
324 if (EINA_UNLIKELY(pol == SCHED_RR || pol == SCHED_FIFO))
325 {
326 param.sched_priority -= RTNICENESS;
327
328 /* We don't change the policy */
329 if (param.sched_priority < 1)
330 {
331 EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
332 param.sched_priority = 1;
333 }
334
335 pthread_setschedparam(pthread_id, pol, &param);
336 }
337# ifdef __linux__
338 else
339 {
340 int prio;
341 errno = 0;
342 prio = getpriority(PRIO_PROCESS, 0);
343 if (errno == 0)
344 {
345 prio += NICENESS;
346 if (prio > 19)
347 {
348 EINA_LOG_INFO("Max niceness reached; keeping max (19)");
349 prio = 19;
350 }
351
352 setpriority(PRIO_PROCESS, 0, prio);
353 }
354 }
355# endif
356}
357
358EINA_API Eina_Bool
359eina_thread_init(void)
360{
361 return EINA_TRUE;
362}
363
364EINA_API Eina_Bool
365eina_thread_shutdown(void)
366{
367 return EINA_TRUE;
368}
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 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2020 Expertise Solutions Cons em Inf
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifdef HAVE_CONFIG_H
20# include "config.h"
21#endif
22
23#include "eina_types.h"
24#include "eina_config.h"
25#include "eina_array.h"
26#include "eina_thread.h"
27#include "eina_main.h"
28#include "eina_debug_private.h"
29#include "eina_log.h"
30
31#include <assert.h>
32#include <evil_private.h>
33#include <process.h>
34
35#define RTNICENESS 1
36#define NICENESS 5
37
38/*
39 * The underlying type of Eina_Thread
40 */
41struct Thread
42{
43 CRITICAL_SECTION cancel_lock; /* mutex to protect the cancel handle */
44 char name[16]; /* the thread name */
45 HANDLE handle; /* thread handle */
46 void *data; /* on entry, the thread function argument, on exit, the return value */
47 Eina_Thread_Cb fn; /* the thread function */
48 Eina_Array *cleanup_fn;
49 Eina_Array *cleanup_arg;
50 unsigned id; /* thread id */
51 Eina_Bool free_on_exit; /* free the structure when thread exit */
52 volatile Eina_Bool cancel; /* the cancel event handle */
53 volatile Eina_Bool cancellable; /* is cancel enabled? */
54};
55
56typedef struct Thread Thread_t;
57
58/*
59 * This TLS stores the Eina_Thread for the current thread
60 */
61static DWORD tls_thread_self = 0;
62
63static Thread_t main_thread = { 0 };
64
65/*
66 * If we alloc'ed the Thread_t in eina_thread_self, we set
67 * free_on_exit flag to true, we then free it here
68 */
69void
70free_thread(void)
71{
72 Thread_t *t = TlsGetValue(tls_thread_self);
73 if (t && t->free_on_exit)
74 {
75 if (t) eina_array_free(t->cleanup_fn);
76 if (t) eina_array_free(t->cleanup_arg);
77 free(t);
78 }
79}
80
81static unsigned
82thread_fn(void *arg)
83{
84 Thread_t *thr = arg;
85 TlsSetValue(tls_thread_self, thr);
86 _eina_debug_thread_add(&thr);
87 EINA_THREAD_CLEANUP_PUSH(_eina_debug_thread_del, &thr);
88 thr->data = thr->fn(thr->data, (Eina_Thread) thr);
89 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
90 return 0;
91}
92
93EINA_API Eina_Thread
94eina_thread_self(void)
95{
96 Thread_t *self = TlsGetValue(tls_thread_self);
97 /*
98 * If self is NULL this means
99 * 1) This function was called before eina_thread_init
100 * 2) This thread wasn't created by eina_thread_create
101 *
102 * In either case we alloc a new Thread struct and return
103 * it.
104 */
105 if (!self)
106 {
107 self = calloc(1, sizeof(*self));
108 self->handle = GetCurrentThread();
109 self->id = GetCurrentThreadId();
110 self->free_on_exit = EINA_TRUE;
111 self->cleanup_fn = eina_array_new(4);
112 self->cleanup_arg = eina_array_new(4);
113 if (tls_thread_self)
114 TlsSetValue(tls_thread_self, self);
115 }
116 return (Eina_Thread) self;
117}
118
119EINA_API Eina_Bool
120eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
121{
122 return ((Thread_t *) t1)->id == ((Thread_t *) t2)->id;
123}
124
125EINA_API Eina_Bool
126eina_thread_create(Eina_Thread *t, Eina_Thread_Priority prio,
127 int affinity, Eina_Thread_Cb func, const void *data)
128{
129 Thread_t *thr = calloc(1, sizeof(Thread_t));
130 if (!thr)
131 return EINA_FALSE;
132
133 thr->data = (void *) data;
134 thr->fn = func;
135
136 thr->handle = (HANDLE) _beginthreadex(NULL, 0, thread_fn, thr, CREATE_SUSPENDED, &thr->id);
137 if (!thr->handle)
138 goto fail;
139
140 int priority;
141 switch (prio)
142 {
143 case EINA_THREAD_URGENT:
144 priority = THREAD_PRIORITY_HIGHEST;
145 break;
146 case EINA_THREAD_BACKGROUND:
147 priority = THREAD_PRIORITY_BELOW_NORMAL;
148 break;
149 case EINA_THREAD_IDLE:
150 priority = THREAD_PRIORITY_IDLE;
151 break;
152 default:
153 priority = THREAD_PRIORITY_NORMAL;
154 }
155
156 if (!SetThreadPriority(thr->handle, priority))
157 goto fail;
158
159 if ((affinity >= 0) && (!SetThreadAffinityMask(thr->handle, 1 << affinity)))
160 goto fail;
161
162 thr->id = GetThreadId(thr->handle);
163 if (!thr->id)
164 goto fail;
165
166 thr->cleanup_fn = eina_array_new(4);
167 thr->cleanup_arg = eina_array_new(4);
168 if ((!thr->cleanup_fn) || (!thr->cleanup_arg))
169 goto fail;
170
171 InitializeCriticalSection(&thr->cancel_lock);
172
173 if (!ResumeThread(thr->handle))
174 goto cs_fail;
175
176 GetModuleFileNameA(NULL, thr->name, sizeof(thr->name));
177 *t = (Eina_Thread) thr;
178 return EINA_TRUE;
179
180cs_fail:
181 DeleteCriticalSection(&thr->cancel_lock);
182fail:
183 if (thr)
184 {
185 if (thr->handle) CloseHandle(thr->handle);
186 if (thr->cleanup_fn) eina_array_free(thr->cleanup_fn);
187 if (thr->cleanup_arg) eina_array_free(thr->cleanup_arg);
188 free(thr);
189 }
190 return EINA_FALSE;
191}
192
193EINA_API void *
194eina_thread_join(Eina_Thread t)
195{
196 void *data;
197 Thread_t *thr = (Thread_t *) t;
198
199 if (WAIT_OBJECT_0 == WaitForSingleObject(thr->handle, INFINITE))
200 data = thr->data;
201 else
202 data = NULL;
203
204 DeleteCriticalSection(&thr->cancel_lock);
205 CloseHandle(thr->handle);
206 eina_array_free(thr->cleanup_fn);
207 eina_array_free(thr->cleanup_arg);
208 free(thr);
209
210 return data;
211}
212
213EINA_API Eina_Bool
214eina_thread_name_set(Eina_Thread t, const char *name)
215{
216 Thread_t *thr = (Thread_t *) t;
217 strncpy(thr->name, name, sizeof(thr->name));
218 thr->name[sizeof(thr->name)-1] = '\0';
219 return EINA_TRUE;
220}
221
222EINA_API Eina_Bool
223eina_thread_cancel(Eina_Thread t)
224{
225 Eina_Bool ret = EINA_FALSE;
226 Thread_t *thr = (Thread_t *) t;
227
228 if (thr)
229 {
230 EnterCriticalSection(&thr->cancel_lock);
231 if (thr->cancellable)
232 {
233 thr->cancel = EINA_TRUE;
234 ret = EINA_TRUE;
235 }
236 LeaveCriticalSection(&thr->cancel_lock);
237 }
238 return ret;
239}
240
241EINA_API Eina_Bool
242eina_thread_cancellable_set(Eina_Bool cancellable, Eina_Bool *was_cancellable)
243{
244 Thread_t *t = (Thread_t *) eina_thread_self();
245
246 EnterCriticalSection(&t->cancel_lock);
247 if (was_cancellable) *was_cancellable = t->cancellable;
248 t->cancellable = cancellable;
249 LeaveCriticalSection(&t->cancel_lock);
250
251 return EINA_TRUE;
252}
253
254EINA_API void
255eina_thread_cancel_checkpoint(void)
256{
257 Eina_Bool cancel;
258 Thread_t *t = (Thread_t *) eina_thread_self();
259
260 EnterCriticalSection(&t->cancel_lock);
261 cancel = t->cancellable && t->cancel;
262 LeaveCriticalSection(&t->cancel_lock);
263
264 if (cancel)
265 {
266 t->data = (void *) EINA_THREAD_JOIN_CANCELED;
267 while (eina_array_count(t->cleanup_fn))
268 {
269 Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn);
270 void *arg = eina_array_pop(t->cleanup_arg);
271
272 if (fn)
273 fn(arg);
274 }
275
276 ExitThread(0);
277 }
278}
279
280EINA_API Eina_Bool
281eina_thread_cleanup_push(Eina_Thread_Cleanup_Cb fn, void *data)
282{
283 Thread_t *t = TlsGetValue(tls_thread_self);
284 assert(t);
285
286 if (!eina_array_push(t->cleanup_fn, fn))
287 return EINA_FALSE;
288
289 if (!eina_array_push(t->cleanup_arg, data))
290 {
291 eina_array_pop(t->cleanup_fn);
292 return EINA_FALSE;
293 }
294
295 return EINA_TRUE;
296}
297
298EINA_API void
299eina_thread_cleanup_pop(int execute)
300{
301 Thread_t *t = TlsGetValue(tls_thread_self);
302 assert(t);
303
304 if (eina_array_count(t->cleanup_fn))
305 {
306 Eina_Thread_Cleanup_Cb fn = (Eina_Thread_Cleanup_Cb) eina_array_pop(t->cleanup_fn);
307 void *arg = eina_array_pop(t->cleanup_arg);
308
309 if (execute && fn)
310 fn(arg);
311 }
312}
313
314EINA_API const void *EINA_THREAD_JOIN_CANCELED = (void *) -1L;
315
316void
317eina_sched_prio_drop(void)
318{
319 Thread_t *thread;
320 int sched_priority;
321
322 thread = (Thread_t *) eina_thread_self();
323
324 sched_priority = GetThreadPriority(thread->handle);
325
326 if (EINA_UNLIKELY(sched_priority == THREAD_PRIORITY_TIME_CRITICAL))
327 {
328 sched_priority -= RTNICENESS;
329
330 /* We don't change the policy */
331 if (sched_priority < 1)
332 {
333 EINA_LOG_INFO("RT prio < 1, setting to 1 instead");
334 sched_priority = 1;
335 }
336 if (!SetThreadPriority(thread->handle, sched_priority))
337 {
338 EINA_LOG_ERR("Unable to query sched parameters");
339 }
340 }
341 else
342 {
343 sched_priority += NICENESS;
344
345 /* We don't change the policy */
346 if (sched_priority > THREAD_PRIORITY_TIME_CRITICAL)
347 {
348 EINA_LOG_INFO("Max niceness reached; keeping max (THREAD_PRIORITY_TIME_CRITICAL)");
349 sched_priority = THREAD_PRIORITY_TIME_CRITICAL;
350 }
351 if (!SetThreadPriority(thread->handle, sched_priority))
352 {
353 EINA_LOG_ERR("Unable to query sched parameters");
354 }
355 }
356}
357
358EINA_API Eina_Bool
359eina_thread_init(void)
360{
361 if (!eina_main_loop_is())
362 return EINA_FALSE;
363
364 tls_thread_self = TlsAlloc();
365 if (TLS_OUT_OF_INDEXES == tls_thread_self)
366 return EINA_FALSE;
367
368 if (!TlsSetValue(tls_thread_self, &main_thread))
369 {
370 assert(0);
371 TlsFree(tls_thread_self);
372 return EINA_FALSE;
373 }
374
375 main_thread.cancellable = EINA_FALSE;
376 main_thread.cancel = EINA_FALSE;
377 main_thread.handle = GetCurrentThread();
378 main_thread.id = GetCurrentThreadId();
379
380 InitializeCriticalSection(&main_thread.cancel_lock);
381 main_thread.cleanup_fn = eina_array_new(2);
382 main_thread.cleanup_arg = eina_array_new(2);
383
384 GetModuleFileNameA(NULL, main_thread.name, sizeof(main_thread.name)/sizeof(main_thread.name[0]));
385
386 return EINA_TRUE;
387}
388
389EINA_API Eina_Bool
390eina_thread_shutdown(void)
391{
392 DeleteCriticalSection(&main_thread.cancel_lock);
393 eina_array_free(main_thread.cleanup_fn);
394 eina_array_free(main_thread.cleanup_arg);
395 TlsFree(tls_thread_self);
396 return EINA_TRUE;
397}
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 @@
1#ifdef HAVE_CONFIG_H
2# include "config.h"
3#endif
4
5#include "eina_config.h"
6#include "eina_types.h"
7#include <evil_private.h>
8
9void free_thread(void);
10
11BOOL WINAPI
12DllMain(HINSTANCE inst EINA_UNUSED, WORD reason, PVOID reserved EINA_UNUSED)
13{
14 if (DLL_THREAD_DETACH == reason)
15 free_thread();
16
17 return TRUE;
18}
diff --git a/src/lib/eina/meson.build b/src/lib/eina/meson.build
index 8d5d4383d1..afe945d754 100644
--- a/src/lib/eina/meson.build
+++ b/src/lib/eina/meson.build
@@ -66,7 +66,6 @@ public_sub_headers = [
66'eina_main.h', 66'eina_main.h',
67'eina_cpu.h', 67'eina_cpu.h',
68'eina_inline_cpu.x', 68'eina_inline_cpu.x',
69'eina_sched.h',
70'eina_tiler.h', 69'eina_tiler.h',
71'eina_hamster.h', 70'eina_hamster.h',
72'eina_matrixsparse.h', 71'eina_matrixsparse.h',
@@ -168,7 +167,6 @@ eina_src = files([
168'eina_rbtree.c', 167'eina_rbtree.c',
169'eina_rectangle.c', 168'eina_rectangle.c',
170'eina_safety_checks.c', 169'eina_safety_checks.c',
171'eina_sched.c',
172'eina_share_common.c', 170'eina_share_common.c',
173'eina_simple_xml_parser.c', 171'eina_simple_xml_parser.c',
174'eina_str.c', 172'eina_str.c',
@@ -200,9 +198,9 @@ eina_src = files([
200]) + eina_mp_sources 198]) + eina_mp_sources
201 199
202if sys_windows == true 200if sys_windows == true
203 eina_src += files('eina_file_win32.c') 201 eina_src += files('eina_file_win32.c', 'eina_win32_dllmain.c', 'eina_thread_win32.c')
204else 202else
205 eina_src += files('eina_file_posix.c') 203 eina_src += files('eina_file_posix.c', 'eina_thread_posix.c')
206endif 204endif
207 205
208eina_config = configuration_data() 206eina_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[] = {
91 { "Vpath", eina_test_vpath }, 91 { "Vpath", eina_test_vpath },
92 { "debug", eina_test_debug }, 92 { "debug", eina_test_debug },
93 { "Abstract Content", eina_test_abstract_content }, 93 { "Abstract Content", eina_test_abstract_content },
94 { "thread", eina_test_thread },
94 { NULL, NULL } 95 { NULL, NULL }
95}; 96};
96 97
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);
79void eina_test_vpath(TCase *tc); 79void eina_test_vpath(TCase *tc);
80void eina_test_debug(TCase *tc); 80void eina_test_debug(TCase *tc);
81void eina_test_abstract_content(TCase *tc); 81void eina_test_abstract_content(TCase *tc);
82void eina_test_thread(TCase *tc);
82 83
83#endif /* EINA_SUITE_H_ */ 84#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 @@
1/* EINA - EFL data type library
2 * Copyright (C) 2020 Expertise Solutions Cons em Inf
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library;
16 * if not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include <check.h>
20#ifdef HAVE_CONFIG_H
21# include "config.h"
22#endif
23
24#ifdef HAVE_UNISTD
25# include <unistd.h>
26#endif
27
28#ifdef _WIN32
29# include <evil_private.h> /* mkdir */
30#endif
31
32#include <Eina.h>
33#include "eina_suite.h"
34
35static void
36thread_cleanup_fn(void *arg)
37{
38 *(int *) arg = 1;
39}
40
41static void *
42thread_fn_execute(void *arg, Eina_Thread t EINA_UNUSED)
43{
44 EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
45 EINA_THREAD_CLEANUP_POP(1);
46 return NULL;
47}
48
49static void *
50thread_fn_skip(void *arg, Eina_Thread t EINA_UNUSED)
51{
52 EINA_THREAD_CLEANUP_PUSH(thread_cleanup_fn, arg);
53 EINA_THREAD_CLEANUP_POP(0);
54 return NULL;
55}
56
57static void *
58thread_fn_cancel(void *arg, Eina_Thread t EINA_UNUSED)
59{
60 Eina_Condition *cond = arg;
61
62 ck_assert(eina_thread_cancellable_set(EINA_TRUE, NULL));
63 ck_assert(eina_condition_signal(cond));
64
65 for (size_t i = 0; i < 100; ++i)
66 {
67 eina_thread_cancel_checkpoint();
68#ifdef _WIN32
69 Sleep(100);
70#else
71 usleep(100 * 1000);
72#endif
73 }
74
75 return NULL;
76}
77
78EFL_START_TEST(eina_thread_test_cleanup_execute)
79{
80 Eina_Thread t;
81 int flag = 0;
82 ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_execute, &flag));
83 eina_thread_join(t);
84 ck_assert_uint_eq(flag, 1);
85}
86EFL_END_TEST
87
88EFL_START_TEST(eina_thread_test_cleanup_skip)
89{
90 Eina_Thread t;
91 int flag = 2;
92 ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_skip, &flag));
93 eina_thread_join(t);
94 ck_assert_uint_eq(flag, 2);
95}
96EFL_END_TEST
97
98EFL_START_TEST(eina_thread_test_cancel)
99{
100 Eina_Thread t;
101 Eina_Lock mutex;
102 Eina_Condition cond;
103
104 ck_assert(eina_lock_new(&mutex));
105 ck_assert(eina_condition_new(&cond, &mutex));
106
107 ck_assert(eina_thread_create(&t, EINA_THREAD_NORMAL, -1, thread_fn_cancel, &cond));
108 ck_assert(eina_lock_take(&mutex));
109 ck_assert(eina_condition_wait(&cond));
110 ck_assert(eina_thread_cancel(t));
111 ck_assert_ptr_eq(eina_thread_join(t), EINA_THREAD_JOIN_CANCELED);
112
113 eina_condition_free(&cond);
114 eina_lock_free(&mutex);
115}
116EFL_END_TEST
117
118void
119eina_test_thread(TCase *tc)
120{
121 tcase_add_test(tc, eina_thread_test_cleanup_skip);
122 tcase_add_test(tc, eina_thread_test_cleanup_execute);
123 tcase_add_test(tc, eina_thread_test_cancel);
124}
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(
56'eina_test_slstr.c', 56'eina_test_slstr.c',
57'eina_test_vpath.c', 57'eina_test_vpath.c',
58'eina_test_abstract_content.c', 58'eina_test_abstract_content.c',
59'eina_test_thread.c',
59) 60)
60 61
61 62