mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-09-11 23:19:30 +00:00
runtime: run syscall/js finalizers on wasm without a manual GC (#5545)
* runtime: run syscall/js finalizers on wasm without a manual GC * runtime: address review feedback on finalizer idle GC * runtime: clear a finished task's args pointer so its arguments are collectable * runtime: skip the finalizer scan with a per-block registration bit * runtime: guard the finalizer registration bitmap with gcLock * testdata: cover finalizer invariants on every scheduler * main_test: limit the finalizer scheduler variants to linux and darwin * testdata: wait for the finalizer queue to drain before asserting * testdata: make the finalizer counters atomic and wait for a known drain count * runtime: add finalizer bookkeeping asserts under runtime_asserts * runtime: address finalizer GC review feedback * testdata: strengthen blocked stack finalizer test * runtime: fix finalizer cleanup edge cases * runtime: decouple wasm export scheduling from finalizers * runtime: avoid redundant wakeups for re-entrant wasm exports * runtime: simplify finalizer comments
This commit is contained in:
@@ -25,6 +25,10 @@ type state struct {
|
||||
stackState
|
||||
|
||||
launched bool
|
||||
|
||||
// finishing marks a goroutine that paused after it completed.
|
||||
// Resume uses this per task flag to clear the stack.
|
||||
finishing bool
|
||||
}
|
||||
|
||||
// stackState is the saved state of a stack while unwound.
|
||||
@@ -42,6 +46,9 @@ type stackState struct {
|
||||
// overwritten. It can be checked from time to time to see whether a stack
|
||||
// overflow happened in the past.
|
||||
canaryPtr *uintptr
|
||||
|
||||
// top marks the end of the stack buffer so it can be cleared after completion.
|
||||
top unsafe.Pointer
|
||||
}
|
||||
|
||||
// start creates and starts a new goroutine with the given function and arguments.
|
||||
@@ -78,6 +85,22 @@ func (s *state) initialize(fn uintptr, args unsafe.Pointer, stackSize uintptr) {
|
||||
// Calculate stack base addresses.
|
||||
s.asyncifysp = unsafe.Add(stack, unsafe.Sizeof(uintptr(0)))
|
||||
s.csp = unsafe.Add(stack, stackSize)
|
||||
s.top = unsafe.Add(stack, stackSize)
|
||||
}
|
||||
|
||||
//go:linkname memzero runtime.memzero
|
||||
func memzero(ptr unsafe.Pointer, size uintptr)
|
||||
|
||||
// MarkFinishing marks the current goroutine for stack cleanup after it returns to the scheduler.
|
||||
func MarkFinishing() {
|
||||
currentTask.state.finishing = true
|
||||
}
|
||||
|
||||
// clearStack removes stale pointers from a finished asyncify stack.
|
||||
// The GC can then collect the stack and referenced objects.
|
||||
func (t *Task) clearStack() {
|
||||
base := unsafe.Pointer(t.state.canaryPtr)
|
||||
memzero(base, uintptr(t.state.top)-uintptr(base))
|
||||
}
|
||||
|
||||
// currentTask is the current running task, or nil if currently in the scheduler.
|
||||
@@ -123,6 +146,12 @@ func (t *Task) Resume() {
|
||||
if uintptr(t.state.asyncifysp) > uintptr(t.state.csp) {
|
||||
runtimeFatal("stack overflow")
|
||||
}
|
||||
if t.state.finishing {
|
||||
// The task is complete. Clear stale stack pointers and release its argument bundle.
|
||||
t.state.finishing = false
|
||||
t.clearStack()
|
||||
t.state.args = nil
|
||||
}
|
||||
}
|
||||
|
||||
//go:linkname saveStackPointer runtime.saveStackPointer
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
//go:build scheduler.tasks
|
||||
|
||||
package task
|
||||
|
||||
// MarkFinishing does nothing for scheduler.tasks because it does not use asyncify heap stacks.
|
||||
func MarkFinishing() {}
|
||||
Reference in New Issue
Block a user