Skip to content

runtime/wasm: support blocking primitives (based on #2197) - #2198

Draft
cpunion wants to merge 28 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-blocking-primitives
Draft

runtime/wasm: support blocking primitives (based on #2197)#2198
cpunion wants to merge 28 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-blocking-primitives

Conversation

@cpunion

@cpunion cpunion commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator

Depends on #2197.

Tracks the B: blocking primitives stage of #2152.

Problem

The single-worker WebAssembly scheduler from #2197 can park and resume goroutines, but channels and the standard sync package still wait through pthread mutexes and condition variables. On the default JS and WASI P1 single-worker runtimes, such a wait blocks the only execution owner, so another runnable goroutine cannot make progress and wake it. It also keeps pthread synchronization coupled to builds that otherwise need no wasm threads.

Implementation

  • Add an opaque scheduler waiter in runtime/internal/runtime; channel/select code uses a small build-specific wait adapter without exposing scheduler-owned g state.
  • Add scheduler-backed semaphore and notify-list queues for default wasm, covering Mutex, RWMutex, WaitGroup, Cond, Once, Pool, sync/atomic.Value, and related internal/sync and internal/poll hooks.
  • Preserve the pthread implementation for native targets and explicit LLGO_WASI_THREADS=1 P1 builds.
  • Split the WaitGroup runtime hook at Go 1.25 because Go 1.24 and Go 1.25+ use different signatures.
  • Apply the existing internal/sync source replacement on Go 1.24+ and avoid non-void defer lowering that crashes LLVM 22 while compiling the upstream hash-trie map implementation.
  • Correct pthread attribute wrapper signatures exposed by the explicit P1 threads acceptance build.
  • Extend the common wasm scheduler fixture with channel/select and synchronization behavior, and reuse the existing CI job rather than adding another matrix lane.

The runtime boundary remains narrow: channel code knows only lock/signal adapters, synchronization code knows only the opaque park/ready handle, and target-specific scheduling stays in the scheduler backend.

Compatibility

  • Default J32, J64, and P1 builds park goroutines and do not use pthread waits.
  • Native Darwin/Linux behavior remains on the existing pthread path.
  • Explicit P1 threads retain the existing pthread scheduler and synchronization path.
  • The implementation supports Go 1.24.2 and Go 1.26.5 without version-dependent runtime ABI mismatches.
  • No existing skip or ignore is added.

Validation

Local tests were run with GOMAXPROCS=2 and -p=1.

  • macOS, Go 1.24.2: runtime module go test ./...; J64 and J32 execution; default P1 execution; explicit P1 threads build and wasm validation.
  • macOS, Go 1.26.5: root-module coverage suite, test/go, runtime module, native test/std/sync, J32/J64/P1 execution, explicit P1 threads build, -O0, -O2, -O3, and ThinLTO.
  • Ubuntu 24.04 ARM64, Go 1.24.2, LLVM 19: internal/build, runtime module, default P1 execution under Wasmtime 39, and explicit P1 threads build/parse validation in a container limited to 15 GiB and 2 CPUs. Peak observed memory remained about 1.2 GiB.
  • Earlier native/embedded regression spot check against the then-current runtime/wasm: add WASI single-worker scheduler (based on #2192, #2208) #2197 head with identical Go 1.26.5/LLVM 19 flags: the cortex-m-qemu empty ELF is byte-identical (132,924-byte file, 4,188-byte text); native hello and sync fixtures add 928 and 920 bytes of loaded sections respectively (about 0.05%), while __text is 108 bytes smaller in both. No wasm scheduler or semaphore symbols are present in the native binaries. Both native fixtures execute successfully; the one-shot sync timing showed no slowdown but is only a smoke check, not a statistical benchmark.
  • The shared fixture covers buffered/unbuffered channels, blocked and ready select cases, close, WaitGroup reuse, Mutex/TryLock, RWMutex, Cond Signal/Broadcast, Once, Pool, atomic Value, and sync.Map operations.
  • The latest main/ssa/wasm: use static defer continuation dispatch #2208 dependency stack passes the full GitHub Ubuntu/macOS matrix and Codecov; J32/J64/P1 blocking fixtures also pass locally.

Relative to #2197, this PR adds four ordered commits across 19 files (+892/-70).

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.93252% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/build/wasm_postlink.go 95.23% 2 Missing and 1 partial ⚠️
internal/build/build.go 85.71% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@cpunion
cpunion force-pushed the codex/wasm-blocking-primitives branch from 18c2966 to c45e243 Compare July 28, 2026 14:15
…orker-asyncify-scheduler

# Conflicts:
#	runtime/internal/runtime/z_default.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant