+++ /dev/null
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <stdlib.h>
-#include <sys/time.h>
-#include <assert.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#if defined(USE_UCONTEXT)
-# include <sys/time.h>
-# include <sys/resource.h>
-# include <ucontext.h>
-#elif defined(USE_SETJMP)
-# include <sys/time.h>
-# if HAVE_SYS_RESOURCE_H
-# include <sys/resource.h>
-# endif
-# include <setjmp.h>
-#endif
-
-#ifdef HAVE_EVIL
-# include <Evil.h>
-#endif
-
-#ifdef _WIN32
-# define USE_FIBERS
-# undef USE_UCONTEXT
-# undef USE_SETJMP
-#endif
-
-#include "Ecore.h"
-#include "ecore_private.h"
-
-typedef struct _Ecore_Coroutine_Defer Ecore_Coroutine_Defer;
-struct _Ecore_Coroutine_Defer
-{
- Eina_Free_Cb func;
- void *data;
-};
-
-struct _Ecore_Coroutine
-{
-#if defined(USE_FIBERS)
- void *context;
-#elif defined(USE_UCONTEXT)
- ucontext_t context;
-#elif defined(USE_SETJMP)
- jmp_buf context;
-#else
-# error "No coroutine implementation !"
-#endif
-
- Eina_Inarray defer;
-
- Ecore_Coroutine_Cb func;
- void *data;
-
- Ecore_Coroutine_State state;
-
-#ifdef USE_VALGRIND
- int vg_stack_id;
-#endif
- int yield_value;
-
- unsigned char stack[1];
-};
-
-#ifdef __x86_64__
-static const int _ecore_coroutine_default_stack_size = 16 * 1024;
-#else
-static const int _ecore_coroutine_default_stack_size = 12 * 1024;
-#endif
-
-static void
-_ecore_coroutine_finish(Ecore_Coroutine *coro)
-{
- int return_value = coro->func(coro->data, coro);
-
- coro->state = ECORE_COROUTINE_FINISHED;
- ecore_coroutine_yield(coro, return_value);
-}
-
-#if defined(USE_UCONTEXT)
-# ifdef __x86_64__
-union ptr_splitter {
- void *ptr;
- uint32_t part[sizeof(void *) / sizeof(uint32_t)];
-};
-
-static void
-_ecore_coroutine_entry_point(uint32_t part0, uint32_t part1)
-{
- union ptr_splitter p = {
- .part = { part0, part1 }
- };
- Ecore_Coroutine *coro = p.ptr;
-
- _ecore_coroutine_finish(coro);
-}
-# else
-static void
-_ecore_coroutine_entry_point(Ecore_Coroutine *coro)
-{
- _ecore_coroutine_finish(coro);
-}
-# endif
-#else
-static void
-_ecore_coroutine_entry_point(Ecore_Coroutine *coro)
-{
- _ecore_coroutine_finish(coro);
-}
-# if defined(USE_SETJMP)
-static void
-_ecore_coroutine_setjmp(Ecore_Coroutine *coro)
-{
- setjmp(coro->context);
-
- /* The idea of this trick come from libcoroutine */
- /* __jmpbuf[6] == stack pointer */
- /* __jmpbuf[7] == program counter */
- coro->context->env[0].__jmpbuf[6] = ((uintptr_t)(&coro->stack));
- coro->context->env[0].__jmpbuf[7] = ((uintptr_t)_ecore_coroutine_entry_point);
-}
-# endif
-#endif
-
-#if defined(USE_FIBERS)
-static void *caller;
-#elif defined(USE_UCONTEXT)
-static ucontext_t caller;
-static ucontext_t callee;
-#elif defined(USE_SETJMP)
-static jmp_buf caller;
-static jmp_buf callee;
-#endif
-
-void
-_ecore_coroutine_init(void)
-{
-#if defined(USE_FIBERS)
- caller = GetCurrentFiber();
- if (caller == (LPVOID) 0x1e00)
- {
- caller = ConvertThreadToFiber(NULL);
- }
-#else
- memset(&caller, 0, sizeof (caller));
- memset(&callee, 0, sizeof (callee));
-#endif
-}
-
-void
-_ecore_coroutine_shutdown(void)
-{
-#ifdef USE_FIBERS
- ConvertFiberToThread();
-#endif
-
- // FIXME: should we track lost coroutine ?
-}
-
-EAPI Ecore_Coroutine *
-ecore_coroutine_add(int stack_size, Ecore_Coroutine_Cb func, void *data)
-{
- Ecore_Coroutine *coro;
-#ifndef USE_FIBERS
- unsigned char *stack;
-#endif
-
- if (stack_size <= 0)
- {
-#if defined(USE_UCONTEXT) || defined(USE_SETJMP)
- struct rlimit check;
-
- if (getrlimit(RLIMIT_STACK, &check))
- check.rlim_cur = _ecore_coroutine_default_stack_size;
- stack_size = check.rlim_cur;
-#elif defined(USE_FIBERS)
- stack_size = _ecore_coroutine_default_stack_size;
-#endif
- if (stack_size < _ecore_coroutine_default_stack_size)
- stack_size = _ecore_coroutine_default_stack_size;
- }
-
- coro = malloc(sizeof (Ecore_Coroutine) + stack_size - 1);
- if (!coro) return NULL;
-
-#ifndef USE_FIBERS
- stack = coro->stack;
-#endif
-
-#ifdef USE_VALGRIND
- coro->vg_stack_id = VALGRIND_STACK_REGISTER(stack, stack + stack_size);
-#endif
-
- coro->state = ECORE_COROUTINE_NEW;
- coro->func = func;
- coro->data = data;
- eina_inarray_step_set(&coro->defer,
- sizeof (Eina_Inarray), sizeof (Ecore_Coroutine_Defer),
- 8);
-
-#if defined(USE_UCONTEXT)
- getcontext(&coro->context);
-
- coro->context.uc_stack.ss_sp = stack;
- coro->context.uc_stack.ss_size = stack_size;
- coro->context.uc_stack.ss_flags = 0;
- coro->context.uc_link = NULL;
-
-# ifdef __x86_64__
- union ptr_splitter p = { .ptr = coro };
- makecontext(&coro->context, (void (*)())_ecore_coroutine_entry_point,
- 2, p.part[0], p.part[1]);
-# else
- makecontext(&coro->context, (void (*)())_ecore_coroutine_entry_point,
- 1, coro);
-# endif
-#elif defined(USE_FIBERS)
- coro->context = CreateFiber(stack_size,
- (LPFIBER_START_ROUTINE)_ecore_coroutine_entry_point,
- coro);
- if (!coro->context)
- {
- free(coro);
- return NULL;
- }
-#elif defined(USE_SETJMP)
- /* We use an intermediate function call to setup the stack with the right arguments */
- _ecore_coroutine_setjmp(coro);
-#endif
-
- return coro;
-}
-
-EAPI void *
-ecore_coroutine_del(Ecore_Coroutine *coro)
-{
- void *data;
-
- data = coro->data;
-
- while (eina_inarray_count(&coro->defer))
- {
- Ecore_Coroutine_Defer *defer;
-
- defer = eina_inarray_pop(&coro->defer);
- defer->func(defer->data);
- }
- eina_inarray_flush(&coro->defer);
-
-#ifdef USE_VALGRIND
- VALGRIND_STACK_DEREGISTER(coro->vg_stack_id);
-#endif
-
-#ifdef USE_FIBERS
- DeleteFiber(coro->context);
-#endif
-
- free(coro);
- return data;
-}
-
-EAPI int
-ecore_coroutine_resume(Ecore_Coroutine *coro)
-{
-#if defined(USE_FIBERS)
- void *prev_caller;
-#elif defined(USE_UCONTEXT)
- ucontext_t prev_caller;
-#elif defined(USE_SETJMP)
- jmp_buf prev_caller;
-#endif
-
- if (coro->state == ECORE_COROUTINE_FINISHED)
- return 0;
- coro->state = ECORE_COROUTINE_RUNNING;
-
- prev_caller = caller;
-#if defined(USE_FIBERS)
- SwitchToFiber(coro->context);
-#elif defined(USE_UCONTEXT)
- swapcontext(&caller, &coro->context);
-#elif defined(USE_SETJMP)
- setjmp(caller);
- longjmp(coro->context);
-#endif
-#ifndef USE_FIBERS
- // As fiber do handle the callee stack for us, no need here
- coro->context = callee;
-#endif
- caller = prev_caller;
-
- return coro->yield_value;
-}
-
-EAPI void
-ecore_coroutine_yield(Ecore_Coroutine *coro, int value)
-{
- coro->yield_value = value;
-#if defined(USE_FIBERS)
- SwitchToFiber(caller);
-#elif defined(USE_UCONTEXT)
- swapcontext(&callee, &caller);
-#elif defined(USE_SETJMP)
- setjmp(callee);
- longjmp(caller);
-#endif
-}
-
-EAPI void *
-ecore_coroutine_data_get(Ecore_Coroutine *coro)
-{
- return coro->data;
-}
-
-EAPI Ecore_Coroutine_State
-ecore_coroutine_state_get(Ecore_Coroutine *coro)
-{
- return coro->state;
-}
-
-EAPI void
-ecore_coroutine_defer(Ecore_Coroutine *coro, Eina_Free_Cb func, void *data)
-{
- Ecore_Coroutine_Defer *defer;
-
- defer = eina_inarray_grow(&coro->defer, 1);
- if (!defer) return;
- defer->func = func;
- defer->data = data;
-}
-
-EAPI void *
-ecore_coroutine_alloc(Ecore_Coroutine *coro, size_t size)
-{
- void *data;
-
- data = malloc(size);
- ecore_coroutine_defer(coro, free, data);
- return data;
-}
-
-
+++ /dev/null
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
-#include <stdio.h>
-#include <unistd.h>
-
-#include <Eina.h>
-#include <Ecore.h>
-
-#include "ecore_suite.h"
-
-typedef struct _Ecore_Coroutine_Test Ecore_Coroutine_Test;
-struct _Ecore_Coroutine_Test
-{
- unsigned int v;
- unsigned int s;
-};
-
-static Ecore_Coroutine_Test t1;
-
-static int
-_ecore_test_v1(void *data, Ecore_Coroutine *coro)
-{
- int *t = data;
-
- ecore_coroutine_yield(coro, 7);
- t1.v = *t;
- ecore_coroutine_yield(coro, 42);
- t1.s = *t;
-
- return 0xDEADBEEF;
-}
-
-static int
-_ecore_test_v2(void *data, Ecore_Coroutine *coro EINA_UNUSED)
-{
- int *t = data;
-
- ecore_coroutine_yield(coro, 42);
- t1.v = *t;
- ecore_coroutine_yield(coro, 7);
- t1.s = *t;
-
- return 0xDEADBEEF;
-}
-
-START_TEST(ecore_test_coroutine_simple)
-{
- Ecore_Coroutine *coro1;
- Ecore_Coroutine *coro2;
- unsigned int value[] = { 7, 42, 0xDEADBEEF };
-
- ecore_init();
-
- t1.v = 0xDEADBEEF; t1.s = 0xDEADBEEF;
-
- coro1 = ecore_coroutine_add(0, _ecore_test_v1, &value[0]);
- coro2 = ecore_coroutine_add(4*1024*1024, _ecore_test_v2, &value[1]);
-
- fail_if(ecore_coroutine_state_get(coro1) != ECORE_COROUTINE_NEW);
- fail_if(ecore_coroutine_resume(coro1) != 7);
- fail_if(t1.v != 0xDEADBEEF || t1.s != 0xDEADBEEF);
- fail_if(ecore_coroutine_resume(coro2) != 42);
- fail_if(t1.v != 0xDEADBEEF || t1.s != 0xDEADBEEF);
- fail_if(ecore_coroutine_resume(coro1) != 42);
- fail_if(t1.v != value[0] || t1.s != 0xDEADBEEF);
- fail_if((unsigned int) ecore_coroutine_resume(coro1) != 0xDEADBEEF);
- fail_if(t1.v != value[0] || t1.s != value[0]);
- fail_if(ecore_coroutine_resume(coro2) != 7);
- fail_if(t1.v != value[1] || t1.s != value[0]);
- fail_if((unsigned int) ecore_coroutine_resume(coro2) != 0xDEADBEEF);
- fail_if(t1.v != value[1] || t1.s != value[1]);
- fail_if(ecore_coroutine_state_get(coro2) != ECORE_COROUTINE_FINISHED);
-
- fail_if(ecore_coroutine_del(coro1) != &value[0]);
- fail_if(ecore_coroutine_del(coro2) != &value[1]);
-
- ecore_shutdown();
-}
-END_TEST
-
-void ecore_test_coroutine(TCase *tc)
-{
- tcase_add_test(tc, ecore_test_coroutine_simple);
-}