eina: add Eina_Thread API.
[profile/ivi/eina.git] / src / lib / eina_thread.c
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 #ifdef HAVE_EVIL
26 # include <Evil.h>
27 #endif
28
29 #include "eina_config.h"
30 #include "eina_thread.h"
31 #include "eina_sched.h"
32
33 #ifdef EINA_HAVE_THREADS
34 # ifdef _WIN32_WCE
35
36 # elif defined(_WIN32)
37
38 #  define WIN32_LEAN_AND_MEAN
39 #  include <windows.h>
40 #  undef WIN32_LEAN_AND_MEAN
41
42 typedef struct _Eina_Thread_Win32 Eina_Thread_Win32;
43 struct _Eina_Thread_Win32
44 {
45    HANDLE thread;
46    Eina_Thread_Cb func;
47    void *data;
48    void *ret;
49
50    Eina_Thread index;
51 };
52
53 /* FIXME: For the moment Eina_Thread is considered not
54    thread safe, wondering if it's worth it */
55 static unsigned long int _current_index = 1; /* start from one as the main loop == 0 */
56 static Eina_List *_thread_pool = NULL;
57 static Eina_List *_thread_running = NULL;
58
59 static Eina_Thread_Win32 *
60 _eina_thread_win32_find(Eina_Thread index)
61 {
62    Eina_Thread_Win32 *tw;
63    Eina_List *l;
64
65    EINA_LIST_FOREACH(_thread_running, l, tw)
66      if (tw->index == index)
67        return tw;
68    return NULL;
69 }
70
71 static Eina_Thread
72 _eina_thread_win32_self(void)
73 {
74    HANDLE t;
75
76    t = GetCurrentThread();
77    EINA_LIST_FOREACH(_thread_running, l, tw)
78      if (tw->thread == t)
79        return tw->index;
80
81    /* We assume main loop == 0 on Windows */
82    return 0;
83 }
84
85 static Eina_Bool
86 _eina_thread_win32_equal(Eina_Thread t1, Eina_Thread t2)
87 {
88    if (t1 == t2) return EINA_TRUE;
89    return EINA_FALSE;
90 }
91
92 static DWORD WINAPI
93 _eina_thread_win32_cb(LPVOID lpParam)
94 {
95    Eina_Thread_Win32 *tw = lpParam;
96
97    tw->ret = tw->func(tw->data, tw->index);
98
99    return 0;
100 }
101
102 static Eina_Bool
103 _eina_thread_win32_create(Eina_Thread *t,
104                           Eina_Thread_Cb func,
105                           const void *data)
106 {
107    Eina_Thread_Win32 *tw;
108    Eina_List *l;
109
110    tw = eina_list_data_get(_thread_pool);
111    _thread_pool = eina_list_remove_list(_thread_pool, _thread_pool);
112
113    if (!tw)
114      {
115         tw = malloc(sizeof (Eina_Thread_Win32));
116         if (!tw) goto on_error;
117
118         do {
119            tw->index = _current_index++;
120         } while (tw->index == 0); /* prevent having a "false" main loop */
121      }
122
123    tw->func = f;
124    tw->data = d;
125
126    tw->thread = CreateThread(NULL, 0, _eina_thread_win32_cb, tw, 0, NULL);
127    if (!tw->thread) goto on_error;
128
129    _thread_running = eina_list_append(_thread_running, tw);
130
131    *t = tw->index;
132    return EINA_TRUE;
133
134  on_error:
135    _thread_pool = eina_list_append(_thread_pool, tw);
136    return EINA_FALSE;   
137 }
138
139 static void *
140 _eina_thread_win32_join(Eina_Thread t)
141 {
142    Eina_Thread_Win32 *tw;
143    void *ret;
144
145    tw = _eina_thread_win32_find(index);
146    if (!tw) return NULL;
147
148    WaitForSingleObject(tw->thread, INFINITE);
149    CloseHandle(tw->thread);
150
151    ret = tw->ret;
152
153    tw->ret = NULL;
154    tw->thread = NULL;
155    tw->func = NULL;
156    tw->data = NULL;
157
158    _thread_running = eina_list_remove(_thread_running, tw);
159    _thread_pool = eina_list_append(_thread_pool, _thread_pool);
160
161    return ret;
162 }
163
164 #  define PHE(x, y)    _eina_thread_win32_equal(x, y)
165 #  define PHS()        _eina_thread_win32_self()
166 #  define PHC(x, f, d) _eina_thread_win32_create(x, f, d)
167 #  define PHJ(x)       _eina_thread_win32_join(x)
168
169 # else
170 #  include <pthread.h>
171
172 #  ifdef __linux__
173 #   include <sched.h>
174 #   include <sys/resource.h>
175 #   include <unistd.h>
176 #   include <sys/syscall.h>
177 #   include <errno.h>
178 #  endif
179
180 static void *
181 _eina_thread_join(Eina_Thread t)
182 {
183    void *ret = NULL;
184
185    if (!pthread_join(t, &ret))
186      return ret;
187    return NULL;
188 }
189
190 #  define PHE(x, y)    pthread_equal(x, y)
191 #  define PHS()        pthread_self()
192 #  define PHC(x, f, d) pthread_create(x, NULL, (void*) f, d)
193 #  define PHJ(x)       _eina_thread_join(x)
194
195 # endif
196 #else
197 # error "Not supported any more"
198 #endif
199
200 typedef struct _Eina_Thread_Call Eina_Thread_Call;
201 struct _Eina_Thread_Call
202 {
203    Eina_Thread_Cb func;
204    const void *data;
205
206    Eina_Thread_Priority prio;
207    Eina_Bool affinity;
208 };
209
210 #include "eina_thread.h"
211
212 static void *
213 _eina_internal_call(void *context)
214 {
215    Eina_Thread_Call *c = context;
216    void *r;
217
218    if (c->prio == EINA_THREAD_BACKGROUND ||
219        c->prio == EINA_THREAD_IDLE)
220      eina_sched_prio_drop();
221
222    /* FIXME: set priority and affinity */
223    r = c->func((void*) c->data, eina_thread_self());
224
225    free(c);
226
227    return r;
228 }
229
230 EAPI Eina_Thread
231 eina_thread_self(void)
232 {
233    return PHS();
234 }
235
236 EAPI Eina_Bool
237 eina_thread_equal(Eina_Thread t1, Eina_Thread t2)
238 {
239    return !!(PHE(t1, t2));
240 }
241
242 EAPI Eina_Bool
243 eina_thread_create(Eina_Thread *t,
244                    Eina_Thread_Priority prio, Eina_Bool affinity,
245                    Eina_Thread_Cb func, const void *data)
246 {
247    Eina_Thread_Call *c;
248
249    c = malloc(sizeof (Eina_Thread_Call));
250    if (!c) return EINA_FALSE;
251
252    c->func = func;
253    c->data = data;
254    c->prio = prio;
255    c->affinity = affinity;
256
257    if (PHC(t, _eina_internal_call, c) == 0)
258      return EINA_TRUE;
259
260    free(c);
261
262    return EINA_FALSE;
263 }
264
265 EAPI void *
266 eina_thread_join(Eina_Thread t)
267 {
268    return PHJ(t);
269 }
270
271 void
272 eina_thread_init(void)
273 {
274 }
275
276 void
277 eina_thread_shutdown(void)
278 {
279 }