summaryrefslogtreecommitdiff
path: root/wasm/fiber.c
blob: ecc481b0ee8d85339d12fd3d88d611d3018e076d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/*
 This is a ucontext-like userland context switching API for WebAssembly based on Binaryen's Asyncify.

 * NOTE:
 * This mechanism doesn't take care of stack state. Just save and restore program counter and
 * registers (rephrased as locals by Wasm term). So use-site need to save and restore the C stack pointer.
 * This Asyncify based implementation is not much efficient and will be replaced with future stack-switching feature.
 */

#include <stdlib.h>
#include "wasm/fiber.h"
#include "wasm/asyncify.h"

#ifdef RB_WASM_ENABLE_DEBUG_LOG
# include <stdio.h>
# define RB_WASM_DEBUG_LOG(...) fprintf(stderr, __VA_ARGS__)
#else
# define RB_WASM_DEBUG_LOG(...)
#endif

void
rb_wasm_init_context(rb_wasm_fiber_context *fcp, void (*func)(void *, void *), void *arg0, void *arg1)
{
    fcp->asyncify_buf.top = &fcp->asyncify_buf.buffer[0];
    fcp->asyncify_buf.end = &fcp->asyncify_buf.buffer[WASM_FIBER_STACK_BUFFER_SIZE];
    fcp->is_rewinding = false;
    fcp->is_started = false;
    fcp->entry_point = func;
    fcp->arg0 = arg0;
    fcp->arg1 = arg1;
    RB_WASM_DEBUG_LOG("[%s] fcp->asyncify_buf %p\n", __func__, &fcp->asyncify_buf);
}

static rb_wasm_fiber_context *_rb_wasm_active_next_fiber;

void
rb_wasm_swapcontext(rb_wasm_fiber_context *ofcp, rb_wasm_fiber_context *fcp)
{
    RB_WASM_DEBUG_LOG("[%s] enter ofcp = %p fcp = %p\n", __func__, ofcp, fcp);
    if (ofcp->is_rewinding) {
        asyncify_stop_rewind();
        ofcp->is_rewinding = false;
        return;
    }
    _rb_wasm_active_next_fiber = fcp;
    RB_WASM_DEBUG_LOG("[%s] start unwinding asyncify_buf = %p\n", __func__, &ofcp->asyncify_buf);
    asyncify_start_unwind(&ofcp->asyncify_buf);
}

void *
rb_wasm_handle_fiber_unwind(void (**new_fiber_entry)(void *, void *),
                            void **arg0, void **arg1, bool *is_new_fiber_started)
{
    rb_wasm_fiber_context *next_fiber;
    if (!_rb_wasm_active_next_fiber) {
        RB_WASM_DEBUG_LOG("[%s] no next fiber\n", __func__);
        *is_new_fiber_started = false;
        return NULL;
    }

    next_fiber = _rb_wasm_active_next_fiber;
    _rb_wasm_active_next_fiber = NULL;

    RB_WASM_DEBUG_LOG("[%s] next_fiber->asyncify_buf = %p\n", __func__, &next_fiber->asyncify_buf);

    *new_fiber_entry = next_fiber->entry_point;
    *arg0 = next_fiber->arg0;
    *arg1 = next_fiber->arg1;

    if (!next_fiber->is_started) {
        RB_WASM_DEBUG_LOG("[%s] new fiber started\n", __func__);
        // start a new fiber if not started yet.
        next_fiber->is_started = true;
        *is_new_fiber_started = true;
        return NULL;
    } else {
        RB_WASM_DEBUG_LOG("[%s] resume a fiber\n", __func__);
        // resume a fiber again
        next_fiber->is_rewinding = true;
        *is_new_fiber_started = false;
        return &next_fiber->asyncify_buf;
    }
}