Ruby 3.5.0dev (2025-01-10 revision 5fab31b15e32622c4b71d1d347a41937e9f9c212)
fiber.c
1/*
2 This is a ucontext-like userland context switching API for WebAssembly based on Binaryen's Asyncify.
3
4 * NOTE:
5 * This mechanism doesn't take care of stack state. Just save and restore program counter and
6 * registers (rephrased as locals by Wasm term). So use-site need to save and restore the C stack pointer.
7 * This Asyncify based implementation is not much efficient and will be replaced with future stack-switching feature.
8 */
9
10#include <stdlib.h>
11#include "wasm/fiber.h"
12#include "wasm/asyncify.h"
13
14#ifdef RB_WASM_ENABLE_DEBUG_LOG
15# include <stdio.h>
16# define RB_WASM_DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
17#else
18# define RB_WASM_DEBUG_LOG(...)
19#endif
20
21void
22rb_wasm_init_context(rb_wasm_fiber_context *fcp, void (*func)(void *, void *), void *arg0, void *arg1)
23{
24 fcp->asyncify_buf.top = &fcp->asyncify_buf.buffer[0];
25 fcp->asyncify_buf.end = &fcp->asyncify_buf.buffer[WASM_FIBER_STACK_BUFFER_SIZE];
26 fcp->is_rewinding = false;
27 fcp->is_started = false;
28 fcp->entry_point = func;
29 fcp->arg0 = arg0;
30 fcp->arg1 = arg1;
31 RB_WASM_DEBUG_LOG("[%s] fcp->asyncify_buf %p\n", __func__, &fcp->asyncify_buf);
32}
33
34static rb_wasm_fiber_context *_rb_wasm_active_next_fiber;
35
36void
37rb_wasm_swapcontext(rb_wasm_fiber_context *ofcp, rb_wasm_fiber_context *fcp)
38{
39 RB_WASM_DEBUG_LOG("[%s] enter ofcp = %p fcp = %p\n", __func__, ofcp, fcp);
40 if (ofcp->is_rewinding) {
41 asyncify_stop_rewind();
42 ofcp->is_rewinding = false;
43 return;
44 }
45 _rb_wasm_active_next_fiber = fcp;
46 RB_WASM_DEBUG_LOG("[%s] start unwinding asyncify_buf = %p\n", __func__, &ofcp->asyncify_buf);
47 asyncify_start_unwind(&ofcp->asyncify_buf);
48}
49
50void *
51rb_wasm_handle_fiber_unwind(void (**new_fiber_entry)(void *, void *),
52 void **arg0, void **arg1, bool *is_new_fiber_started)
53{
54 rb_wasm_fiber_context *next_fiber;
55 if (!_rb_wasm_active_next_fiber) {
56 RB_WASM_DEBUG_LOG("[%s] no next fiber\n", __func__);
57 *is_new_fiber_started = false;
58 return NULL;
59 }
60
61 next_fiber = _rb_wasm_active_next_fiber;
62 _rb_wasm_active_next_fiber = NULL;
63
64 RB_WASM_DEBUG_LOG("[%s] next_fiber->asyncify_buf = %p\n", __func__, &next_fiber->asyncify_buf);
65
66 *new_fiber_entry = next_fiber->entry_point;
67 *arg0 = next_fiber->arg0;
68 *arg1 = next_fiber->arg1;
69
70 if (!next_fiber->is_started) {
71 RB_WASM_DEBUG_LOG("[%s] new fiber started\n", __func__);
72 // start a new fiber if not started yet.
73 next_fiber->is_started = true;
74 *is_new_fiber_started = true;
75 return NULL;
76 } else {
77 RB_WASM_DEBUG_LOG("[%s] resume a fiber\n", __func__);
78 // resume a fiber again
79 next_fiber->is_rewinding = true;
80 *is_new_fiber_started = false;
81 return &next_fiber->asyncify_buf;
82 }
83}