Skip to content

runtime/wasm: add bounded Web worker scheduling (based on #2214) - #2216

Draft
cpunion wants to merge 65 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-bounded-workers
Draft

runtime/wasm: add bounded Web worker scheduling (based on #2214)#2216
cpunion wants to merge 65 commits into
xgo-dev:mainfrom
cpunion:codex/wasm-bounded-workers

Conversation

@cpunion

@cpunion cpunion commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Depends on #2214.

Implements the M1 bounded-Web-worker stage of #2152.

Problem

The cooperative js/wasm scheduler from #2214 can multiplex goroutines, but all Go work still runs on one host thread. CPU-bound goroutines cannot overlap, and simply creating one pthread per goroutine would make resource use unbounded.

Design

  • LLGO_WASM_WORKERS=N opts into a fixed Emscripten pthread pool (2 <= N <= 16); the default value 1 is inert.
  • Each worker permanently owns one M, one P, and one locked run queue. A G is assigned round-robin at creation and remains on that owner, so getg and runtime_procPin are O(1).
  • Worker 0 owns host events and deadlines. Cross-worker timer changes wake it through an atomic sequence plus Emscripten futex.
  • Channels, semaphores, notify lists, timers, and scheduler waiters use worker-safe synchronization. The Ready-before-Park path keeps the G active until an early notification is resolved, avoiding transient false-deadlock reports.
  • Emscripten provides main; LLGo exposes __main_argc_argv and runs package init/main as the schedulable main G.
  • The browser host shim supports Node and cross-origin-isolated browsers. CI serves browser artifacts with COOP/COEP headers.

Build configuration lives in internal/wasmworkers; the runtime host boundary lives in runtime/internal/wasmworkers. Existing single-worker files are selected unchanged through build tags.

The current collector is deliberately disabled when workers are enabled. Explicitly combining llgo_wasm_gc with LLGO_WASM_WORKERS>1 is rejected until the M2 allocator/STW work lands.

Validation

  • macOS arm64: J32 and J64 Node stress, 10/10 each; Chrome COOP/COEP acceptance, both passed.
  • Ubuntu 24.04 arm64 container: 2 CPU, 15 GiB hard memory limit, no swap; J32 and J64 Node stress, 10/10 each.
  • Each stress run covers two truly overlapping pthreads, stable G/M/P ownership, 5,000 G lifecycles, 100,000 cross-worker channel handoffs, all configured workers, Mutex, WaitGroup, atomic operations, and timer wakeups.
  • An additional 30-run channel/benchmark stress loop passed after fixing the Ready-before-Park active-count race.
  • internal/wasmworkers: 100% statement coverage.
  • configureWasmWorkers: 88.2%; configureWasmGC: 95.8%; worker entry generation: 100%.
  • Full internal/build, internal/crosscompile, and runtime module tests passed on macOS and Ubuntu.
  • The full Ubuntu/macOS CI matrix and Codecov patch check pass.
  • Default native and wasm builds remain functionally unchanged. A Cortex-M0 empty image is exactly unchanged at text/data/bss 132/0/10.

Median of 7 J32 runs in the same constrained Ubuntu container:

Benchmark #2214 single worker M1, 2 workers
G lifecycle 755,844 ns/op 15,480 ns/op
Channel round trip 3,112 ns/op 18,340 ns/op
One CPU job 53,795,146 ns/op 34,432,768 ns/op
Two CPU jobs, per job 72,524,582 ns/op 19,943,296 ns/op

These are stage-level measurements, not a scheduler-only comparison: M1 temporarily disables the collector pending M2. The worker host adds about 29 KiB of generated J32 JavaScript; non-worker builds add no generated runtime code or embedded cost.

cpunion added 30 commits July 28, 2026 12:56
cpunion added 25 commits July 29, 2026 07:46
…orker-asyncify-scheduler

# Conflicts:
#	internal/build/source_patch_test.go
…orker-asyncify-scheduler

# Conflicts:
#	runtime/internal/runtime/z_default.go
# Conflicts:
#	.github/workflows/llgo.yml
@codecov

codecov Bot commented Jul 29, 2026

Copy link
Copy Markdown

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