Ruby 3.5.0dev (2025-01-10 revision 5fab31b15e32622c4b71d1d347a41937e9f9c212)
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 <stdint.h>
17#include "wasm/asyncify.h"
18#include "wasm/machine.h"
19#include "wasm/fiber.h"
20
21#define COROUTINE void __attribute__((__noreturn__))
22
23static const int ASYNCIFY_CORO_DEBUG = 0;
24
26{
28 void *argument;
29 struct coroutine_context *from;
30
31 void *current_sp;
32 void *stack_base;
33 size_t size;
34};
35
36typedef COROUTINE(* coroutine_start)(struct coroutine_context *from, struct coroutine_context *self);
37
38COROUTINE coroutine_trampoline(void * _start, void * _context);
39
40static inline void coroutine_initialize_main(struct coroutine_context * context)
41{
42 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p)\n", __func__, context);
43 // NULL fiber entry means it's the main fiber, and handled specially.
44 rb_wasm_init_context(&context->fc, NULL, NULL, NULL);
45 // mark the main fiber has already started
46 context->fc.is_started = true;
47}
48
49static inline void coroutine_initialize(struct coroutine_context *context, coroutine_start start, void *stack, size_t size)
50{
51 // Linear stack pointer must be always aligned down to 16 bytes.
52 // https://github.com/WebAssembly/tool-conventions/blob/c74267a5897c1bdc9aa60adeaf41816387d3cd12/BasicCABI.md#the-linear-stack
53 uintptr_t sp = ((uintptr_t)stack + size) & ~0xF;
54 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p, stack = %p ... %p)\n", __func__, context, stack, (char *)sp);
55 rb_wasm_init_context(&context->fc, coroutine_trampoline, start, context);
56 // record the initial stack pointer position to restore it after resumption
57 context->current_sp = (char *)sp;
58 context->stack_base = stack;
59 context->size = size;
60}
61
62static inline struct coroutine_context * coroutine_transfer(struct coroutine_context * current, struct coroutine_context * target)
63{
64 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (current = %p, target = %p)\n", __func__, current, target);
65 struct coroutine_context * previous = target->from;
66
67 target->from = current;
68 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] current->current_sp = %p -> %p\n", __func__, current->current_sp, rb_wasm_get_stack_pointer());
69 // record the current stack pointer position to restore it after resumption
70 current->current_sp = rb_wasm_get_stack_pointer();
71
72 // suspend the current coroutine and resume another coroutine
73
74 rb_wasm_swapcontext(&current->fc, &target->fc);
75
76 // after the original coroutine resumed
77
78 rb_wasm_set_stack_pointer(current->current_sp);
79
80 target->from = previous;
81
82 return target;
83}
84
85static inline void coroutine_destroy(struct coroutine_context * context)
86{
87 if (ASYNCIFY_CORO_DEBUG) fprintf(stderr, "[%s] entry (context = %p)\n", __func__, context);
88 context->stack_base = NULL;
89 context->size = 0;
90 context->from = NULL;
91}
92
93#endif /* COROUTINE_ASYNCIFY_CONTEXT_H */