Ruby 3.3.0p0 (2023-12-25 revision 5124f9ac7513eb590c37717337c430cb93caa151)
Context.h
1#ifndef COROUTINE_ASYNCIFY_CONTEXT_H
2#define COROUTINE_ASYNCIFY_CONTEXT_H
3
4/*
5 This is a coroutine implementation based on Binaryen's Asyncify transformation for WebAssembly.
6
7 This implementation is built on low-level ucontext-like API in wasm/fiber.c
8 This file is an adapter for the common coroutine interface and for stack manipulation.
9 wasm/fiber.c doesn't take care of stack to avoid duplicate management with this adapter.
10
11 * See also: wasm/fiber.c
12*/
13
14#include <stddef.h>
15#include <stdio.h>
16#include "wasm/asyncify.h"
17#include "wasm/machine.h"
18#include "wasm/fiber.h"
19
20#define COROUTINE void __attribute__((__noreturn__))
21
22static const int ASYNCIFY_CORO_DEBUG = 0;
23
25{
27 void *argument;
28 struct coroutine_context *from;
29
30 void *current_sp;
31 void *stack_base;
32 size_t size;
33};
34
35typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
36
37COROUTINE coroutine_trampoline(void * _start, void * _context);
38
39static inline void coroutine_initialize_main(struct coroutine_context * context)
40{
41 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p)\n", __func__, context);
42 // NULL fiber entry means it's the main fiber, and handled specially.
43 rb_wasm_init_context(&context->fc, NULL, NULL, NULL);
44 // mark the main fiber has already started
45 context->fc.is_started = true;
46}
47
48static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
49{
50 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)stack + size);
51 rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
52 // record the initial stack pointer position to restore it after resumption
53 context->current_sp = (char *)stack + size;
54 context->stack_base = stack;
55 context->size = size;
56}
57
58static inline struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target)
59{
60 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (current = %p, target = %p)\n", __func__, current, target);
61 struct coroutine_context * previous = target->from;
62
63 target->from = current;
64 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] current->current_sp = %p -> %p\n", __func__, current->current_sp, rb_wasm_get_stack_pointer());
65 // record the current stack pointer position to restore it after resumption
66 current->current_sp = rb_wasm_get_stack_pointer();
67
68 // suspend the current coroutine and resume another coroutine
69
70 rb_wasm_swapcontext(&current->fc, &target->fc);
71
72 // after the original coroutine resumed
73
74 rb_wasm_set_stack_pointer(current->current_sp);
75
76 target->from = previous;
77
78 return target;
79}
80
81static inline void coroutine_destroy(struct coroutine_context * context)
82{
83 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p)\n", __func__, context);
84 context->stack_base = NULL;
85 context->size = 0;
86 context->from = NULL;
87}
88
89#endif /* COROUTINE_ASYNCIFY_CONTEXT_H */