summaryrefslogtreecommitdiff
path: root/src/lib/ecore
diff options
context:
space:
mode:
authorCedric BAIL <cedric@efl.so>2013-02-18 14:32:51 +0100
committerCedric BAIL <cedric@efl.so>2013-02-18 14:38:33 +0100
commit91711d8a694e53a95d164645b774de568ae4322b (patch)
treef9779e4e7d2efee19a1242f5c8efd5ea61c4f808 /src/lib/ecore
parentceed029a84f695e93290addede0c8104bfbc8187 (diff)
ecore: add Ecore_Coroutine.
That work clearly was possible thanks to Leandro. If you want more information go to his blog : http://tia.mat.br/posts/async_io_with_coroutines/ . The main difference with his implementation is more portable and not thread safe. It does not have a custom swapcontext (would make sense as we don't need to save the sigcontext) so it will be less fast. If people are ready to contribute asm patch for that purpose I will be happy to apply them. As for portability this code should work on all architecture we already support thanks to a nice hack with setjmp/longjmp borowed from libcoroutine. We do use Fiber for Windows support, but as 1.8 is completely borken in that regard, this is theorical work only. Thinks left to do : - Eoify the API - Documentation - More tests - Add support for coroutine in fd handler - Add coroutine support to ecore_thread api - Write some example
Diffstat (limited to 'src/lib/ecore')
-rw-r--r--src/lib/ecore/Ecore.h21
-rw-r--r--src/lib/ecore/ecore.c4
-rw-r--r--src/lib/ecore/ecore_coroutine.c339
-rw-r--r--src/lib/ecore/ecore_private.h3
4 files changed, 366 insertions, 1 deletions
diff --git a/src/lib/ecore/Ecore.h b/src/lib/ecore/Ecore.h
index 34210f932e..2f44326dad 100644
--- a/src/lib/ecore/Ecore.h
+++ b/src/lib/ecore/Ecore.h
@@ -2779,6 +2779,27 @@ EAPI void *ecore_thread_global_data_wait(const char *key, double seconds);
2779 * @} 2779 * @}
2780 */ 2780 */
2781 2781
2782typedef struct _Ecore_Coroutine Ecore_Coroutine;
2783typedef int (*Ecore_Coroutine_Cb)(void *data, Ecore_Coroutine *coro);
2784
2785typedef enum {
2786 ECORE_COROUTINE_NEW,
2787 ECORE_COROUTINE_RUNNING,
2788 ECORE_COROUTINE_FINISHED
2789} Ecore_Coroutine_State;
2790
2791EAPI Ecore_Coroutine *ecore_coroutine_add(int stack_size, Ecore_Coroutine_Cb func, void *data);
2792EAPI void *ecore_coroutine_del(Ecore_Coroutine *coro);
2793
2794EAPI int ecore_coroutine_resume(Ecore_Coroutine *coro);
2795EAPI void ecore_coroutine_yield(Ecore_Coroutine *coro, int value);
2796
2797EAPI void *ecore_coroutine_data_get(Ecore_Coroutine *coro);
2798EAPI Ecore_Coroutine_State ecore_coroutine_state_get(Ecore_Coroutine *coro);
2799
2800EAPI void ecore_coroutine_defer(Ecore_Coroutine *coro, Eina_Free_Cb func, void *data);
2801EAPI void *ecore_coroutine_alloc(Ecore_Coroutine *coro, size_t size);
2802
2782/** 2803/**
2783 * @defgroup Ecore_Pipe_Group Pipe wrapper 2804 * @defgroup Ecore_Pipe_Group Pipe wrapper
2784 * 2805 *
diff --git a/src/lib/ecore/ecore.c b/src/lib/ecore/ecore.c
index 5c9ad0cd28..3c5a1019a0 100644
--- a/src/lib/ecore/ecore.c
+++ b/src/lib/ecore/ecore.c
@@ -166,6 +166,7 @@ ecore_init(void)
166 _ecore_glib_init(); 166 _ecore_glib_init();
167 _ecore_job_init(); 167 _ecore_job_init();
168 _ecore_time_init(); 168 _ecore_time_init();
169 _ecore_coroutine_init();
169 170
170 eina_lock_new(&_thread_mutex); 171 eina_lock_new(&_thread_mutex);
171 eina_condition_new(&_thread_cond, &_thread_mutex); 172 eina_condition_new(&_thread_cond, &_thread_mutex);
@@ -234,8 +235,9 @@ ecore_shutdown(void)
234 } 235 }
235 if (--_ecore_init_count != 0) 236 if (--_ecore_init_count != 0)
236 goto unlock; 237 goto unlock;
237 238
238 if (_ecore_fps_debug) _ecore_fps_debug_shutdown(); 239 if (_ecore_fps_debug) _ecore_fps_debug_shutdown();
240 _ecore_coroutine_shutdown();
239 _ecore_poller_shutdown(); 241 _ecore_poller_shutdown();
240 _ecore_animator_shutdown(); 242 _ecore_animator_shutdown();
241 _ecore_glib_shutdown(); 243 _ecore_glib_shutdown();
diff --git a/src/lib/ecore/ecore_coroutine.c b/src/lib/ecore/ecore_coroutine.c
new file mode 100644
index 0000000000..676141f3ac
--- /dev/null
+++ b/src/lib/ecore/ecore_coroutine.c
@@ -0,0 +1,339 @@
1#ifdef HAVE_CONFIG_H
2# include <config.h>
3#endif
4
5#include <stdlib.h>
6#include <sys/time.h>
7#include <assert.h>
8#include <sys/types.h>
9#include <unistd.h>
10
11#if defined(USE_UCONTEXT)
12# include <sys/time.h>
13# include <sys/resource.h>
14# include <ucontext.h>
15#elif defined(USE_SETJMP)
16# include <sys/time.h>
17# include <sys/resource.h>
18# include <setjmp.h>
19#endif
20
21#ifdef HAVE_EVIL
22# include <Evil.h>
23#endif
24
25#ifdef _WIN32
26# define USE_FIBERS
27#endif
28
29#include "Ecore.h"
30#include "ecore_private.h"
31
32typedef struct _Ecore_Coroutine_Defer Ecore_Coroutine_Defer;
33struct _Ecore_Coroutine_Defer
34{
35 Eina_Free_Cb func;
36 void *data;
37};
38
39struct _Ecore_Coroutine
40{
41#if defined(USE_FIBERS)
42 void *context;
43#elif defined(USE_UCONTEXT)
44 ucontext_t context;
45#elif defined(USE_SETJMP)
46 jmp_buf context;
47#else
48# error "No coroutine implementation !"
49#endif
50
51 Eina_Inarray defer;
52
53 Ecore_Coroutine_Cb func;
54 void *data;
55
56 Ecore_Coroutine_State state;
57
58#ifdef USE_VALGRIND
59 int vg_stack_id;
60#endif
61 int yield_value;
62
63 unsigned char stack[1];
64};
65
66#ifdef __x86_64__
67static const int const _ecore_coroutine_default_stack_size = 16 * 1024;
68#else
69static const int const _ecore_coroutine_default_stack_size = 12 * 1024;
70#endif
71
72static void
73_ecore_coroutine_finish(Ecore_Coroutine *coro)
74{
75 int return_value = coro->func(coro->data, coro);
76
77 coro->state = ECORE_COROUTINE_FINISHED;
78 ecore_coroutine_yield(coro, return_value);
79}
80
81#if defined(USE_UCONTEXT)
82# ifdef __x86_64__
83union ptr_splitter {
84 void *ptr;
85 uint32_t part[sizeof(void *) / sizeof(uint32_t)];
86};
87
88static void
89_ecore_coroutine_entry_point(uint32_t part0, uint32_t part1)
90{
91 union ptr_splitter p = {
92 .part = { part0, part1 }
93 };
94 Ecore_Coroutine *coro = p.ptr;
95
96 _ecore_coroutine_finish(coro);
97}
98# else
99static void
100_ecore_coroutine_entry_point(Ecore_Coroutine *coro)
101{
102 _ecore_coroutine_finish(coro);
103}
104# endif
105#else
106static void
107_ecore_coroutine_entry_point(Ecore_Coroutine *coro)
108{
109 _ecore_coroutine_finish(coro);
110}
111# if defined(USE_SETJMP)
112static void
113_ecore_coroutine_setjmp(Ecore_Coroutine *coro)
114{
115 setjmp(coro->context);
116
117 /* The idea of this trick come from libcoroutine */
118 /* __jmpbuf[6] == stack pointer */
119 /* __jmpbuf[7] == program counter */
120 self->env[0].__jmpbuf[6] = ((uintptr_t)(&coro->stack));
121 self->env[0].__jmpbuf[7] = ((uintptr_t)_ecore_coroutine_entry_point);
122}
123# endif
124#endif
125
126#if defined(USE_FIBERS)
127static void *caller;
128static void *callee;
129#elif defined(USE_UCONTEXT)
130static ucontext_t caller;
131static ucontext_t callee;
132#elif defined(USE_SETJMP)
133static jmp_buf caller;
134static jmp_buf callee;
135#endif
136
137void
138_ecore_coroutine_init(void)
139{
140#if defined(USE_FIBERS)
141 caller = GetCurrentFiber();
142 if (caller == (LPVOID) 0x1e00)
143 {
144 caller = ConvertThreadToFiber(NULL);
145 }
146#else
147 memset(&caller, 0, sizeof (caller));
148 memset(&callee, 0, sizeof (callee));
149#endif
150}
151
152void
153_ecore_coroutine_shutdown(void)
154{
155#ifdef USE_FIBERS
156 ConvertFiberToThread();
157#endif
158
159 // FIXME: should we track lost coroutine ?
160}
161
162EAPI Ecore_Coroutine *
163ecore_coroutine_add(int stack_size, Ecore_Coroutine_Cb func, void *data)
164{
165 Ecore_Coroutine *coro;
166 unsigned char *stack;
167
168 if (stack_size <= 0)
169 {
170#if defined(USE_UCONTEXT) || defined(USE_SETJMP)
171 struct rlimit check;
172
173 if (getrlimit(RLIMIT_STACK, &check))
174 check.rlim_cur = _ecore_coroutine_default_stack_size;
175 stack_size = check.rlim_cur;
176#elif defined(USE_FIBERS)
177 stack_size = _ecore_coroutine_default_stack_size;
178#endif
179 if (stack_size < _ecore_coroutine_default_stack_size)
180 stack_size = _ecore_coroutine_default_stack_size;
181 }
182
183 coro = malloc(sizeof (Ecore_Coroutine) + stack_size - 1);
184 if (!coro) return NULL;
185
186 stack = coro->stack;
187
188#ifdef USE_VALGRIND
189 coro->vg_stack_id = VALGRIND_STACK_REGISTER(stack, stack + stack_size);
190#endif
191
192 coro->state = ECORE_COROUTINE_NEW;
193 coro->func = func;
194 coro->data = data;
195 eina_inarray_step_set(&coro->defer,
196 sizeof (Eina_Inarray), sizeof (Ecore_Coroutine_Defer),
197 8);
198
199#if defined(USE_UCONTEXT)
200 getcontext(&coro->context);
201
202 coro->context.uc_stack.ss_sp = stack;
203 coro->context.uc_stack.ss_size = stack_size;
204 coro->context.uc_stack.ss_flags = 0;
205 coro->context.uc_link = NULL;
206
207# ifdef __x86_64__
208 union ptr_splitter p = { .ptr = coro };
209 makecontext(&coro->context, (void (*)())_ecore_coroutine_entry_point,
210 2, p.part[0], p.part[1]);
211# else
212 makecontext(&coro->context, (void (*)())_ecore_coroutine_entry_point,
213 1, coro);
214# endif
215#elif defined(USE_FIBERS)
216 coro->context = CreateFiber(stack_size,
217 (LPFIBER_START_ROUTINE)_ecore_coroutine_entry_point,
218 coro);
219 if (!coro->context)
220 {
221 free(coro);
222 return NULL;
223 }
224#elif defined(USE_SETJMP)
225 /* We use an intermediate function call to setup the stack with the right arguments */
226 _ecore_coroutine_setjmp(coro);
227#endif
228
229 return coro;
230}
231
232EAPI void *
233ecore_coroutine_del(Ecore_Coroutine *coro)
234{
235 void *data;
236
237 data = coro->data;
238
239 while (eina_inarray_count(&coro->defer))
240 {
241 Ecore_Coroutine_Defer *defer;
242
243 defer = eina_inarray_pop(&coro->defer);
244 defer->func(defer->data);
245 }
246 eina_inarray_flush(&coro->defer);
247
248#ifdef USE_VALGRIND
249 VALGRIND_STACK_DEREGISTER(coro->vg_stack_id);
250#endif
251
252#ifdef USE_FIBERS
253 DeleteFiber(coro->context);
254#endif
255
256 free(coro);
257 return data;
258}
259
260EAPI int
261ecore_coroutine_resume(Ecore_Coroutine *coro)
262{
263#if defined(USE_FIBERS)
264 void *prev_caller;
265#elif defined(USE_UCONTEXT)
266 ucontext_t prev_caller;
267#elif defined(USE_SETJMP)
268 jmp_buf prev_caller;
269#endif
270
271 if (coro->state == ECORE_COROUTINE_FINISHED)
272 return 0;
273 coro->state = ECORE_COROUTINE_RUNNING;
274
275 prev_caller = caller;
276#if defined(USE_FIBERS)
277 SwitchToFiber(coro->context);
278#elif defined(USE_UCONTEXT)
279 swapcontext(&caller, &coro->context);
280#elif defined(USE_SETJMP)
281 setjmp(caller);
282 longjmp(coro->context);
283#endif
284#ifndef USE_FIBERS
285 // As fiber do handle the callee stack for us, no need here
286 coro->context = callee;
287#endif
288 caller = prev_caller;
289
290 return coro->yield_value;
291}
292
293EAPI void
294ecore_coroutine_yield(Ecore_Coroutine *coro, int value)
295{
296 coro->yield_value = value;
297#if defined(USE_FIBERS)
298 SwitchToFiber(caller);
299#elif defined(USE_UCONTEXT)
300 swapcontext(&callee, &caller);
301#elif defined(USE_SETJMP)
302 setjmp(callee);
303 longjmp(caller);
304#endif
305}
306
307EAPI void *
308ecore_coroutine_data_get(Ecore_Coroutine *coro)
309{
310 return coro->data;
311}
312
313EAPI Ecore_Coroutine_State
314ecore_coroutine_state_get(Ecore_Coroutine *coro)
315{
316 return coro->state;
317}
318
319EAPI void
320ecore_coroutine_defer(Ecore_Coroutine *coro, Eina_Free_Cb func, void *data)
321{
322 Ecore_Coroutine_Defer *defer;
323
324 defer = eina_inarray_grow(&coro->defer, 1);
325 defer->func = func;
326 defer->data = data;
327}
328
329EAPI void *
330ecore_coroutine_alloc(Ecore_Coroutine *coro, size_t size)
331{
332 void *data;
333
334 data = malloc(size);
335 ecore_coroutine_defer(coro, free, data);
336 return data;
337}
338
339
diff --git a/src/lib/ecore/ecore_private.h b/src/lib/ecore/ecore_private.h
index b9c11b5da8..1c7f53e5a4 100644
--- a/src/lib/ecore/ecore_private.h
+++ b/src/lib/ecore/ecore_private.h
@@ -238,6 +238,9 @@ void _ecore_job_shutdown(void);
238void _ecore_main_loop_init(void); 238void _ecore_main_loop_init(void);
239void _ecore_main_loop_shutdown(void); 239void _ecore_main_loop_shutdown(void);
240 240
241void _ecore_coroutine_init(void);
242void _ecore_coroutine_shutdown(void);
243
241void _ecore_throttle(void); 244void _ecore_throttle(void);
242 245
243void _ecore_main_call_flush(void); 246void _ecore_main_call_flush(void);