svn update: 48958 (latest:48959)
[framework/uifw/ecore.git] / src / lib / ecore / ecore_thread.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #ifdef HAVE_EVIL
6 # include <Evil.h>
7 #endif
8
9 #ifdef EFL_HAVE_PTHREAD
10 # include <pthread.h>
11 #endif
12
13 #include "Ecore.h"
14 #include "ecore_private.h"
15
16 #ifdef EFL_HAVE_PTHREAD
17 typedef struct _Ecore_Pthread_Worker Ecore_Pthread_Worker;
18 typedef struct _Ecore_Pthread_Data Ecore_Pthread_Data;
19 typedef struct _Ecore_Pthread Ecore_Pthread;
20
21 struct _Ecore_Pthread_Worker
22 {
23    void (*func_heavy)(void *data);
24    void (*func_end)(void *data);
25    void (*func_cancel)(void *data);
26
27    const void *data;
28
29    Eina_Bool cancel : 1;
30 };
31
32 struct _Ecore_Pthread_Data
33 {
34    Ecore_Pipe *p;
35    pthread_t thread;
36 };
37 #endif
38
39 static int _ecore_thread_count_max = 0;
40 static int ECORE_THREAD_PIPE_DEL = 0;
41
42 #ifdef EFL_HAVE_PTHREAD
43 static int _ecore_thread_count = 0;
44 static Eina_List *_ecore_thread_data = NULL;
45 static Eina_List *_ecore_thread = NULL;
46 static Ecore_Event_Handler *del_handler = NULL;
47
48 static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
49
50 static void
51 _ecore_thread_pipe_free(void *data __UNUSED__, void *event)
52 {
53    Ecore_Pipe *p = event;
54
55    ecore_pipe_del(p);
56 }
57
58 static int
59 _ecore_thread_pipe_del(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__)
60 {
61    /* This is a hack to delay pipe destruction until we are out of it's internal loop. */
62    return 0;
63 }
64
65 static void
66 _ecore_thread_end(Ecore_Pthread_Data *pth)
67 {
68    Ecore_Pipe *p;
69
70    if (pthread_join(pth->thread, (void**) &p) != 0)
71      return ;
72
73    _ecore_thread = eina_list_remove(_ecore_thread, pth);
74
75    ecore_event_add(ECORE_THREAD_PIPE_DEL, pth->p, _ecore_thread_pipe_free, NULL);
76 }
77
78 static void *
79 _ecore_thread_worker(Ecore_Pthread_Data *pth)
80 {
81    Ecore_Pthread_Worker *work;
82
83    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
84    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
85
86    pthread_mutex_lock(&_mutex);
87    _ecore_thread_count++;
88    pthread_mutex_unlock(&_mutex);
89
90  on_error:
91
92    while (_ecore_thread_data)
93      {
94         pthread_mutex_lock(&_mutex);
95
96         if (!_ecore_thread_data)
97           {
98              pthread_mutex_unlock(&_mutex);
99              break;
100           }
101
102         work = eina_list_data_get(_ecore_thread_data);
103         _ecore_thread_data = eina_list_remove_list(_ecore_thread_data, _ecore_thread_data);
104
105         pthread_mutex_unlock(&_mutex);
106
107         work->func_heavy((void*) work->data);
108
109         ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker*));
110      }
111
112    pthread_mutex_lock(&_mutex);
113    if (_ecore_thread_data)
114      {
115         pthread_mutex_unlock(&_mutex);
116         goto on_error;
117      }
118    _ecore_thread_count--;
119
120    pthread_mutex_unlock(&_mutex);
121
122    work = malloc(sizeof (Ecore_Pthread_Worker));
123    if (!work) return NULL;
124
125    work->data = pth;
126    work->func_heavy = NULL;
127    work->func_end = (void*) _ecore_thread_end;
128    work->func_cancel = NULL;
129    work->cancel = EINA_FALSE;
130
131    ecore_pipe_write(pth->p, &work, sizeof (Ecore_Pthread_Worker*));
132
133    return pth->p;
134 }
135
136 static void
137 _ecore_thread_handler(void *data __UNUSED__, void *buffer, unsigned int nbyte)
138 {
139    Ecore_Pthread_Worker *work;
140
141    if (nbyte != sizeof (Ecore_Pthread_Worker*)) return ;
142
143    work = *(Ecore_Pthread_Worker**)buffer;
144
145    if (work->cancel)
146      {
147         if (work->func_cancel)
148           work->func_cancel((void*) work->data);
149      }
150    else
151      {
152         work->func_end((void*) work->data);
153      }
154
155    free(work);
156 }
157 #endif
158
159 void
160 _ecore_thread_init(void)
161 {
162    _ecore_thread_count_max = eina_cpu_count();
163    if (_ecore_thread_count_max <= 0)
164      _ecore_thread_count_max = 1;
165
166    ECORE_THREAD_PIPE_DEL = ecore_event_type_new();
167 #ifdef EFL_HAVE_PTHREAD
168    del_handler = ecore_event_handler_add(ECORE_THREAD_PIPE_DEL, _ecore_thread_pipe_del, NULL);
169 #endif
170 }
171
172 void
173 _ecore_thread_shutdown(void)
174 {
175    /* FIXME: If function are still running in the background, should we kill them ? */
176 #ifdef EFL_HAVE_PTHREAD
177    Ecore_Pthread_Worker *work;
178    Ecore_Pthread_Data *pth;
179
180    pthread_mutex_lock(&_mutex);
181
182    EINA_LIST_FREE(_ecore_thread_data, work)
183      {
184         if (work->func_cancel)
185           work->func_cancel((void*)work->data);
186         free(work);
187      }
188
189    pthread_mutex_unlock(&_mutex);
190
191    EINA_LIST_FREE(_ecore_thread, pth)
192      {
193         Ecore_Pipe *p;
194
195         pthread_cancel(pth->thread);
196         pthread_join(pth->thread, (void **) &p);
197
198         ecore_pipe_del(pth->p);
199      }
200
201    ecore_event_handler_del(del_handler);
202    del_handler = NULL;
203 #endif
204 }
205
206 /*
207  * ecore_thread_run provide a facility for easily managing heavy task in a
208  * parallel thread. You should provide two function, the first one, func_heavy,
209  * that will do the heavy work in another thread (so you should not use the
210  * EFL in it except Eina if you are carefull), and the second one, func_end,
211  * that will be called in Ecore main loop when func_heavy is done. So you
212  * can use all the EFL inside this function.
213  *
214  * Be aware, that you can't make assumption on the result order of func_end
215  * after many call to ecore_thread_run, as we start as much thread as the
216  * host CPU can handle.
217  */
218 EAPI Ecore_Thread *
219 ecore_thread_run(void (*func_heavy)(void *data),
220                  void (*func_end)(void *data),
221                  void (*func_cancel)(void *data),
222                  const void *data)
223 {
224 #ifdef EFL_HAVE_PTHREAD
225    Ecore_Pthread_Worker *work;
226    Ecore_Pthread_Data *pth;
227
228    work = malloc(sizeof (Ecore_Pthread_Worker));
229    if (!work)
230      {
231         func_cancel((void*) data);
232         return NULL;
233      }
234
235    work->func_heavy = func_heavy;
236    work->func_end = func_end;
237    work->func_cancel = func_cancel;
238    work->cancel = EINA_FALSE;
239    work->data = data;
240
241    pthread_mutex_lock(&_mutex);
242    _ecore_thread_data = eina_list_append(_ecore_thread_data, work);
243
244    if (_ecore_thread_count == _ecore_thread_count_max)
245      {
246         pthread_mutex_unlock(&_mutex);
247         return (Ecore_Thread*) work;
248      }
249
250    pthread_mutex_unlock(&_mutex);
251
252    /* One more thread could be created. */
253    pth = malloc(sizeof (Ecore_Pthread_Data));
254    if (!pth)
255      goto on_error;
256
257    pth->p = ecore_pipe_add(_ecore_thread_handler, NULL);
258
259    if (pthread_create(&pth->thread, NULL, (void*) _ecore_thread_worker, pth) == 0)
260      return (Ecore_Thread*) work;
261
262  on_error:
263    if (_ecore_thread_count == 0)
264      {
265         if (work->func_cancel)
266           work->func_cancel((void*) work->data);
267         free(work);
268      }
269    return NULL;
270 #else
271    /*
272      If no thread and as we don't want to break app that rely on this
273      facility, we will lock the interface until we are done.
274     */
275    func_heavy((void *)data);
276    func_end((void *)data);
277
278    return NULL;
279 #endif
280 }
281
282 /*
283  * ecore_thread_cancel give the possibility to cancel a task still running. It
284  * will return EINA_FALSE, if the destruction is delayed or EINA_TRUE if it is
285  * cancelled after this call.
286  *
287  * You should use this function only in the main loop.
288  *
289  * func_end, func_cancel will destroy the handler, so don't use it after.
290  * And if ecore_thread_cancel return EINA_TRUE, you should not use Ecore_Thread also.
291  */
292 EAPI Eina_Bool
293 ecore_thread_cancel(Ecore_Thread *thread)
294 {
295 #ifdef EFL_HAVE_PTHREAD
296    Ecore_Pthread_Worker *work;
297    Eina_List *l;
298
299    pthread_mutex_lock(&_mutex);
300
301    EINA_LIST_FOREACH(_ecore_thread_data, l, work)
302      if ((void*) work == (void*) thread)
303        {
304           _ecore_thread_data = eina_list_remove_list(_ecore_thread_data, l);
305
306           pthread_mutex_unlock(&_mutex);
307
308           if (work->func_cancel)
309             work->func_cancel((void*) work->data);
310           free(work);
311
312           return EINA_TRUE;
313        }
314
315    pthread_mutex_unlock(&_mutex);
316
317    /* Delay the destruction */
318    ((Ecore_Pthread_Worker*)thread)->cancel = EINA_TRUE;
319    return EINA_FALSE;
320 #else
321    return EINA_TRUE;
322 #endif
323 }