From 62283f7a7a87fa2f353814a3ef6a3b1cab2a32b8 Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Sat, 23 Jan 2021 18:54:50 +0900 Subject: [PATCH] coroutine/emscripten/: Experimentally support emscripten fiber API --- configure.ac | 6 ++- coroutine/emscripten/Context.c | 8 ++++ coroutine/emscripten/Context.h | 76 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 coroutine/emscripten/Context.c create mode 100644 coroutine/emscripten/Context.h diff --git a/configure.ac b/configure.ac index 05d3e993c5..285a2f9bcd 100644 --- a/configure.ac +++ b/configure.ac @@ -1138,7 +1138,8 @@ main() ]) ], [emscripten*], [LIBS="-lm -lc $LIBS" - RUBY_APPEND_OPTIONS(LDFLAGS, -s ALLOW_MEMORY_GROWTH=1) + RUBY_APPEND_OPTIONS(LDFLAGS, "-s ALLOW_MEMORY_GROWTH=1") + RUBY_APPEND_OPTIONS(LDFLAGS, "-s ASYNCIFY") ], [ LIBS="-lm $LIBS"]) : ${ORIG_LIBS=$LIBS} @@ -2461,6 +2462,9 @@ AS_CASE([$rb_cv_coroutine], [yes|''], [ [*-haiku*], [ rb_cv_coroutine=copy ], + [*-emscripten*], [ + rb_cv_coroutine=emscripten + ], [ rb_cv_coroutine=ucontext ] diff --git a/coroutine/emscripten/Context.c b/coroutine/emscripten/Context.c new file mode 100644 index 0000000000..75c088daaa --- /dev/null +++ b/coroutine/emscripten/Context.c @@ -0,0 +1,8 @@ +#include "Context.h" + +void coroutine_trampoline(void * _context) +{ + struct coroutine_context * context = _context; + + context->entry_func(context->from, context); +} diff --git a/coroutine/emscripten/Context.h b/coroutine/emscripten/Context.h new file mode 100644 index 0000000000..aefbb92900 --- /dev/null +++ b/coroutine/emscripten/Context.h @@ -0,0 +1,76 @@ +#ifndef COROUTINE_EMSCRIPTEN_CONTEXT_H +#define COROUTINE_EMSCRIPTEN_CONTEXT_H 1 + +/* An experimental coroutine wrapper for emscripten + * Contact on Yusuke Endoh if you encounter any problem about this + */ + +#pragma once + +#include +#include +#include + +#define COROUTINE __attribute__((noreturn)) void + +#if INTPTR_MAX <= INT32_MAX +#define COROUTINE_LIMITED_ADDRESS_SPACE +#endif + +struct coroutine_context; + +typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self); + +struct coroutine_context +{ + emscripten_fiber_t state; + coroutine_start entry_func; + struct coroutine_context * from; +}; + +COROUTINE coroutine_trampoline(void * _context); + +#define MAIN_ASYNCIFY_STACK_SIZE 65536 +static inline void coroutine_initialize_main(struct coroutine_context * context) { + static char asyncify_stack[MAIN_ASYNCIFY_STACK_SIZE]; + emscripten_fiber_init_from_current_context(&context->state, asyncify_stack, MAIN_ASYNCIFY_STACK_SIZE); +} +#undef MAIN_ASYNCIFY_STACK_SIZE + +static inline void coroutine_initialize( + struct coroutine_context *context, + coroutine_start start, + void *stack, + size_t size +) { + assert(start && stack && size >= 1024); + + uintptr_t addr = (uintptr_t)stack; + size_t offset = addr & 0xF; + void *c_stack = (void*)((addr + 0xF) & ~0xF); + size -= offset; + size_t c_stack_size = (size / 2) & ~0xF; + void *asyncify_stack = (void*)((uintptr_t)c_stack + c_stack_size); + size_t asyncify_stack_size = size - c_stack_size; + context->entry_func = start; + + emscripten_fiber_init(&context->state, coroutine_trampoline, context, c_stack, c_stack_size, asyncify_stack, asyncify_stack_size); +} + +static inline struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target) +{ + struct coroutine_context * previous = target->from; + + target->from = current; + emscripten_fiber_swap(¤t->state, &target->state); + target->from = previous; + + return target; +} + +static inline void coroutine_destroy(struct coroutine_context * context) +{ + context->from = NULL; +} + +#endif /* COROUTINE_EMSCRIPTEN_CONTEXT_H */