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_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
50 static inline void *
_eina_thread_join(Eina_Thread t)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
60 static inline Eina_Bool
_eina_thread_create(Eina_Thread * t,int affinity,void * (* func)(void * data),void * data)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
112 static inline Eina_Bool
_eina_thread_equal(Eina_Thread t1,Eina_Thread t2)113 _eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
114 {
115 return pthread_equal((pthread_t)t1, (pthread_t)t2);
116 }
117
118 static inline Eina_Thread
_eina_thread_self(void)119 _eina_thread_self(void)
120 {
121 return (Eina_Thread)pthread_self();
122 }
123
124
125 typedef struct _Eina_Thread_Call Eina_Thread_Call;
126 struct _Eina_Thread_Call
127 {
128 Eina_Thread_Cb func;
129 const void *data;
130
131 Eina_Thread_Priority prio;
132 int affinity;
133 };
134
135 static void *
_eina_internal_call(void * context)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, ¶ms);
158 #endif
159 min = sched_get_priority_min(pol);
160 params.sched_priority = min;
161 pthread_setschedparam(self, pol, ¶ms);
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, ¶ms);
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, ¶ms);
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, ¶ms);
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, ¶ms);
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
200 return r;
201 }
202
203 EAPI Eina_Thread
eina_thread_self(void)204 eina_thread_self(void)
205 {
206 return _eina_thread_self();
207 }
208
209 EAPI Eina_Bool
eina_thread_equal(Eina_Thread t1,Eina_Thread t2)210 eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
211 {
212 return !!_eina_thread_equal(t1, t2);
213 }
214
215 EAPI Eina_Bool
eina_thread_create(Eina_Thread * t,Eina_Thread_Priority prio,int affinity,Eina_Thread_Cb func,const void * data)216 eina_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
242 EAPI void *
eina_thread_join(Eina_Thread t)243 eina_thread_join(Eina_Thread t)
244 {
245 return _eina_thread_join(t);
246 }
247
248 EAPI Eina_Bool
eina_thread_name_set(Eina_Thread t,const char * name)249 eina_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
272 EAPI Eina_Bool
eina_thread_cancel(Eina_Thread t)273 eina_thread_cancel(Eina_Thread t)
274 {
275 if (!t) return EINA_FALSE;
276 return pthread_cancel((pthread_t)t) == 0;
277 }
278
279 EAPI Eina_Bool
eina_thread_cancellable_set(Eina_Bool cancellable,Eina_Bool * was_cancellable)280 eina_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
296 EAPI void
eina_thread_cancel_checkpoint(void)297 eina_thread_cancel_checkpoint(void)
298 {
299 pthread_testcancel();
300 }
301
302 EAPI void *
eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb,Eina_Free_Cb cleanup_cb,void * data)303 eina_thread_cancellable_run(Eina_Thread_Cancellable_Run_Cb cb, Eina_Free_Cb cleanup_cb, void *data)
304 {
305 Eina_Bool old = EINA_FALSE;
306 void *ret;
307
308 EINA_THREAD_CLEANUP_PUSH(cleanup_cb, data);
309 eina_thread_cancellable_set(EINA_TRUE, &old); // is a cancellation point
310 ret = cb(data); // may not run if was previously canceled
311 EINA_THREAD_CLEANUP_POP(EINA_TRUE);
312 eina_thread_cancellable_set(old, NULL);
313 return ret;
314 }
315
316 EAPI const void *EINA_THREAD_JOIN_CANCELED = PTHREAD_CANCELED;
317
318 Eina_Bool
eina_thread_init(void)319 eina_thread_init(void)
320 {
321 return EINA_TRUE;
322 }
323
324 Eina_Bool
eina_thread_shutdown(void)325 eina_thread_shutdown(void)
326 {
327 return EINA_TRUE;
328 }
329