internal/task: use asyncify on webassembly

This change implements a new "scheduler" for WebAssembly using binaryen's asyncify transform.
This is more reliable than the current "coroutines" transform, and works with non-Go code in the call stack.

runtime (js/wasm): handle scheduler nesting

If WASM calls into JS which calls back into WASM, it is possible for the scheduler to nest.
The event from the callback must be handled immediately, so the task cannot simply be deferred to the outer scheduler.
This creates a minimal scheduler loop which is used to handle such nesting.
This commit is contained in:
Nia Waldvogel
2021-07-21 14:30:01 -04:00
committed by Ron Evans
parent 523d1f28cf
commit 641dcd7c16
31 changed files with 785 additions and 62 deletions
+99
View File
@@ -0,0 +1,99 @@
.globaltype __stack_pointer, i32
.global tinygo_unwind
.type tinygo_unwind,@function
tinygo_unwind: // func (state *stackState) unwind()
.functype tinygo_unwind (i32) -> ()
// Check if we are rewinding.
i32.const 0
i32.load8_u tinygo_rewinding
if // if tinygo_rewinding {
// Stop rewinding.
call stop_rewind
i32.const 0
i32.const 0
i32.store8 tinygo_rewinding // tinygo_rewinding = false;
else
// Save the C stack pointer (destination structure pointer is in local 0).
local.get 0
global.get __stack_pointer
i32.store 4 // state.csp = getCurrentStackPointer()
// Ask asyncify to unwind.
// When resuming, asyncify will return this function with tinygo_rewinding set to true.
local.get 0
call start_unwind // asyncify.start_unwind(state)
end_if
return
end_function
.global tinygo_launch
.type tinygo_launch,@function
tinygo_launch: // func (state *state) launch()
.functype tinygo_launch (i32) -> ()
// Switch to the goroutine's C stack.
global.get __stack_pointer // prev := getCurrentStackPointer()
local.get 0
i32.load 12
global.set __stack_pointer // setStackPointer(state.csp)
// Get the argument pack and entry pointer.
local.get 0
i32.load 4 // args := state.args
local.get 0
i32.load 0 // fn := state.entry
// Launch the entry function.
call_indirect (i32) -> () // fn(args)
// Stop unwinding.
call stop_unwind
// Restore the C stack.
global.set __stack_pointer // setStackPointer(prev)
return
end_function
.global tinygo_rewind
.type tinygo_rewind,@function
tinygo_rewind: // func (state *state) rewind()
.functype tinygo_rewind (i32) -> ()
// Switch to the goroutine's C stack.
global.get __stack_pointer // prev := getCurrentStackPointer()
local.get 0
i32.load 12
global.set __stack_pointer // setStackPointer(state.csp)
// Get the argument pack and entry pointer.
local.get 0
i32.load 4 // args := state.args
local.get 0
i32.load 0 // fn := state.entry
// Prepare to rewind.
i32.const 0
i32.const 1
i32.store8 tinygo_rewinding // tinygo_rewinding = true;
local.get 0
i32.const 8
i32.add
call start_rewind // asyncify.start_rewind(&state.stackState)
// Launch the entry function.
// This will actually rewind the call stack.
call_indirect (i32) -> () // fn(args)
// Stop unwinding.
call stop_unwind
// Restore the C stack.
global.set __stack_pointer // setStackPointer(prev)
return
end_function
.functype start_unwind (i32) -> ()
.import_module start_unwind, asyncify
.functype stop_unwind () -> ()
.import_module stop_unwind, asyncify
.functype start_rewind (i32) -> ()
.import_module start_rewind, asyncify
.functype stop_rewind () -> ()
.import_module stop_rewind, asyncify
.hidden tinygo_rewinding # @tinygo_rewinding
.type tinygo_rewinding,@object
.section .bss.tinygo_rewinding,"",@
.globl tinygo_rewinding
tinygo_rewinding:
.int8 0 # 0x0
.size tinygo_rewinding, 1