mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-07 04:23:41 +00:00
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:
@@ -1,3 +1,4 @@
|
||||
//go:build gc.conservative
|
||||
// +build gc.conservative
|
||||
|
||||
package runtime
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build wasm && !wasi
|
||||
// +build wasm,!wasi
|
||||
|
||||
package runtime
|
||||
@@ -6,13 +7,19 @@ import "unsafe"
|
||||
|
||||
type timeUnit float64 // time in milliseconds, just like Date.now() in JavaScript
|
||||
|
||||
// wasmNested is used to detect scheduler nesting (WASM calls into JS calls back into WASM).
|
||||
// When this happens, we need to use a reduced version of the scheduler.
|
||||
var wasmNested bool
|
||||
|
||||
//export _start
|
||||
func _start() {
|
||||
// These need to be initialized early so that the heap can be initialized.
|
||||
heapStart = uintptr(unsafe.Pointer(&heapStartSymbol))
|
||||
heapEnd = uintptr(wasm_memory_size(0) * wasmPageSize)
|
||||
|
||||
wasmNested = true
|
||||
run()
|
||||
wasmNested = false
|
||||
}
|
||||
|
||||
var handleEvent func()
|
||||
@@ -27,12 +34,27 @@ func resume() {
|
||||
go func() {
|
||||
handleEvent()
|
||||
}()
|
||||
|
||||
if wasmNested {
|
||||
minSched()
|
||||
return
|
||||
}
|
||||
|
||||
wasmNested = true
|
||||
scheduler()
|
||||
wasmNested = false
|
||||
}
|
||||
|
||||
//export go_scheduler
|
||||
func go_scheduler() {
|
||||
if wasmNested {
|
||||
minSched()
|
||||
return
|
||||
}
|
||||
|
||||
wasmNested = true
|
||||
scheduler()
|
||||
wasmNested = false
|
||||
}
|
||||
|
||||
func ticksToNanoseconds(ticks timeUnit) int64 {
|
||||
|
||||
@@ -172,6 +172,24 @@ func scheduler() {
|
||||
}
|
||||
}
|
||||
|
||||
// This horrible hack exists to make WASM work properly.
|
||||
// When a WASM program calls into JS which calls back into WASM, the event with which we called back in needs to be handled before returning.
|
||||
// Thus there are two copies of the scheduler running at once.
|
||||
// This is a reduced version of the scheduler which does not deal with the timer queue (that is a problem for the outer scheduler).
|
||||
func minSched() {
|
||||
scheduleLog("start nested scheduler")
|
||||
for !schedulerDone {
|
||||
t := runqueue.Pop()
|
||||
if t == nil {
|
||||
break
|
||||
}
|
||||
|
||||
scheduleLogTask(" run:", t)
|
||||
t.Resume()
|
||||
}
|
||||
scheduleLog("stop nested scheduler")
|
||||
}
|
||||
|
||||
func Gosched() {
|
||||
runqueue.Push(task.Current())
|
||||
task.Pause()
|
||||
|
||||
Reference in New Issue
Block a user