diff --git a/wit/runtime/runtime.go b/wit/runtime/runtime.go index 9871799..fc2caea 100644 --- a/wit/runtime/runtime.go +++ b/wit/runtime/runtime.go @@ -109,6 +109,19 @@ func Unpin() { //go:wasmimport wasi_snapshot_preview1 adapter_monotonic_clock_set_paused func adapterMonotonicClockSetPaused(paused bool) +// `procPin` increments the current M's lock count, which prevents the Go +// runtime from starting a GC cycle or performing GC assist work until +// `procUnpin` is called. The runtime explicitly supports linking to these; +// see https://go.dev/issue/67401. + +//nolint:unused +//go:linkname procPin runtime.procPin +func procPin() int + +//nolint:unused +//go:linkname procUnpin runtime.procUnpin +func procUnpin() + //nolint:unused //go:wasmexport cabi_realloc func cabiRealloc(oldPointer unsafe.Pointer, oldSize, align, newSize uintptr) unsafe.Pointer { @@ -117,19 +130,25 @@ func cabiRealloc(oldPointer unsafe.Pointer, oldSize, align, newSize uintptr) uns } if useGCAllocations { - // Here we call `adapter_monotonic_clock_set_paused` before and - // after allocating since the Go garbage collector calls - // `clock_time_get` to measure time spent in various stages of - // GC, but calls to imports from `cabi_realloc` are forbidden by - // the component model, so we must tell the - // `wasi_snapshot_preview1` adapter to use a cached value - // instead of calling `monotonic_clock::now`. + // Calls to imports from `cabi_realloc` are forbidden by the + // component model, so we must not let the Go garbage collector + // run here. A GC cycle may call imports in several ways: it + // reads the monotonic and realtime clocks, polls for I/O when + // restarting the world, and may switch to other goroutines + // (e.g. to park an assist), any of which may then call imports. + // Pinning the M defers any GC work until the next allocation + // made outside of `cabi_realloc`. // - // See https://github.com/bytecodealliance/wasmtime/pull/13563 - // for more details. + // We additionally call `adapter_monotonic_clock_set_paused` + // before and after allocating in case anything else reads the + // monotonic clock while allocating. See + // https://github.com/bytecodealliance/wasmtime/pull/13563 for + // details. + procPin() adapterMonotonicClockSetPaused(true) pointer := Allocate(&pinner, newSize, align) adapterMonotonicClockSetPaused(false) + procUnpin() return pointer } else { alignedSize := newSize + offset(newSize, align)