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, &params);
158 #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 
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