runtime/wasm: stop all workers for garbage collection (based on #2216) - #2217
Draft
cpunion wants to merge 68 commits into
Draft
runtime/wasm: stop all workers for garbage collection (based on #2216)#2217cpunion wants to merge 68 commits into
cpunion wants to merge 68 commits into
Conversation
# Conflicts: # .github/workflows/llgo.yml
…orker-asyncify-scheduler # Conflicts: # runtime/internal/runtime/z_default.go
…/wasm-wasi-single-worker
…nts-roots-v2 # Conflicts: # cl/compile.go
# Conflicts: # .github/workflows/llgo.yml
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This was referenced Jul 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #2216.
Implements the M2 multi-worker GC stage of #2152.
Problem
#2216 deliberately disables the collector when
LLGO_WASM_WORKERS>1. The existing compiler root-chain slot and active-context state are process globals, the allocator is unsynchronized, and one worker can collect while another worker is still executing with unpublished wasm-local roots. Enabling GC without this PR can therefore corrupt allocator metadata or reclaim objects that remain live on another worker.Design
//llgo:tlsstorage and keeps every system/G context in a synchronized global enumeration registry.__libc_malloc,calloc,realloc, andfreeentry points are routed to the selected collector whenMALLOC=none.LLGO_WASM_WORKERS>1now enables the same default collector/root planning as single-worker J32/J64. Native, embedded, single-worker wasm, and explicit P1 thread selection remain source-isolated by existing build tags.The compiler owns only root-frame emission and TLS selection.
runtime/internal/gcrootowns execution-context root publication;runtime/internal/runtimeowns worker/STW policy;runtime/internal/runtime/tinygogcowns collector hooks; and the smallruntime/internal/wasmsyncpackage owns the GC-cooperative mutex.Validation
internal/buildcoverage tests pass as a non-root user. The runtime and affected compiler/build tests pass.configureWasmGCis 100% covered. Every new SSA TLS/root-chain function is 100% covered; runtimegcrootis 94.1% covered.go build -p=1 ./..., affected compiler tests, runtime module tests, andgit diff --checkpass.The J64 stress process can print Emscripten's post-exit “user callback triggered after runtime exited” diagnostic after the success line; it exits 0 and does not occur in the GC fixture. Shutdown hardening remains part of H rather than being hidden by a retry or skip.
Resource and size results
Against #2216, the same two-worker stress fixture grows by 267,854 bytes for J32 (1,556,088 -> 1,823,942) and 360,614 bytes for J64 (1,710,749 -> 2,071,363). Generated JS size is byte-identical; the wasm increase is the collector, root publication, synchronized allocator, and STW machinery that M1 intentionally omitted.
On five macOS J64 runs, the fixture median moves from 0.72 s on M1 to 2.31 s on M2. This is a stage-level comparison, not a pure STW benchmark: M1 uses Emscripten allocation with no collector, while M2 performs compiler root publication and GC-managed allocation. The single-worker wasm comparison changes by only +516 bytes for J32 and +4,029 bytes for J64, isolating most of the direct size increase to enabling the collector in worker builds. Allocator/GC throughput optimization remains in H after correctness is established.
A representative native binary has identical text/data section sizes against #2216. The Cortex-M0 empty image is byte-for-byte identical and remains text/data/bss
132/0/10.