zig-js supports two complementary thread models:
| Model | What shares | What crosses | Primary docs |
|---|---|---|---|
| Agent / worker isolation | No JS heap state; each OS thread owns a Context, global object, jobs, allocator state, and exception state. |
Structured-clone bytes and retained SharedArrayBuffer storage. |
Agents, Workers |
Shared-realm Thread |
One Context, global object, heap, shape tree, and object identity. |
Same-realm function arguments and return values. | Thread API, Phase 6, GIL removal |
The shared-realm model is now true-parallel by default:
const ctx = try js.Context.createWith(gpa, .{ .enable_threads = true });That installs Thread, Lock, Condition, ThreadLocal,
ConcurrentAccessError, property-mode Atomics.*, and proposal-aligned
Atomics.Mutex / Atomics.Condition. Spawned Threads run JavaScript
concurrently on real OS threads over the GC-managed, thread-safe heap.
The serialized fallback is still supported when deterministic GIL interleavings or legacy compatibility are useful:
const ctx = try js.Context.createWith(gpa, .{
.enable_threads = true,
.gil = true,
});The C API exposes the same choice with ZJSGlobalContextCreateThreaded(gil).
Non-threaded contexts remain single-threaded and keep the original affinity
rules.
| Area | Status | Verification |
|---|---|---|
Refcounted SharedArrayBuffer storage |
Implemented in src/shared_buffer.zig and typed-array storage in src/value.zig. |
Unit tests, test262 SAB / Atomics shards, TSan gates. |
| WebAssembly shared memory and atomic execution | Complete atomic opcode execution, SeqCst RMW/CAS/fence, wait32/wait64/notify, fixed historical Memory buffers, and targeted termination interruption. Terminal proposal-script/TSan/scaling evidence remains in issue #287. | Pinned threads/atomic.wast 372/372, 1,069-test root, focused overlapping-access TSan witnesses. |
$262.agent and typed-array Atomics.wait / notify / waitAsync |
Implemented in src/agent.zig with hooks in the interpreter and VM. |
Unit tests and real test262 agent cases. |
| Structured clone and ArrayBuffer transfer/detach | Implemented in src/structured_clone.zig. |
Unit tests, workers, and agents. |
Embedder Worker API |
Implemented in src/worker.zig with C-API hooks in src/c_api.zig. |
Worker unit tests, exact host-hook wake coverage, and C-API round trips. |
Shared-realm Thread API |
Implemented in src/jsthread.zig, src/gil.zig, and src/context.zig; parallel by default, GIL opt-out available. |
PR-249 coverage: 248 promoted of 259 executable files (243 default plus 5 parallel_js-only); the inventory checksums all 339 files and distinguishes 5 optimizing-tier blockers from 6 terminal premises. Disposition probes, no-GIL TSan, and fuzzers guard the surface. |
| Concurrent GC / root safety | GC-managed parallel contexts use thread-safe allocation, write barriers, per-structure locks, precise VM frame roots, and conservative native-stack rooting where applicable. | Unit tests, parallel_gc soak, no-GIL corpus TSan, test262-parallel. |
Context.createWith(.{ .enable_threads = false })installs noThreadglobals and keeps the original single-thread affinity rule.Context.createWith(.{ .enable_threads = true })runs shared-realmThreads in parallel by default and implies the GC-managed, thread-safe cell path.Context.createWith(.{ .enable_threads = true, .gil = true })keeps the same JavaScript API but serializes execution behind the context GIL.- Blocking APIs (
join,Lock,Condition, typed-array Atomics wait, and property-mode Atomics wait) use their own synchronization paths in no-GIL mode and release the context GIL in serialized mode. - Object shapes, named properties, elements/collections, environments, promises, microtasks, inline caches, thread records, waiter queues, and shared-buffer storage each have explicit synchronization. New mutable shared state must follow that pattern.
- JavaScript program races are distinct from engine-state races. See Memory Model for the public contract and the ThreadSanitizer suppression boundary.
- Test-only knobs such as
parallel_jsandparallel_midscript_gcremain internal harness controls. They are not stable embedder APIs. - Process-global mutable state must be listed in bindings.md
with a
per-thread,locked, orrefusedruling.
- Thread API - supported shared-realm JavaScript surface.
- Testing - exact Zig
0.17-devverification commands and CI gates. - Memory Model - JS program races, engine-state races, and the TSan suppression boundary.
- GC Relocation Contract - the audited pointer, stable identity, forwarding, rewrite, and pinning boundary for moving collection.
- Production Readiness - current no-GIL status and remaining hardening work.
- Limits & Roadmap - unsupported surfaces, test-only knobs, and remaining performance/coverage goals.
- bindings.md - mutable-state audit and contribution rule.
- GitHub issue #1 - concise umbrella tracker. Detailed acceptance criteria live in its linked GC, contention, mid-script-GC, fuzzing, PR-249, and memory-model child issues.
- P2-agents.md, P5-workers.md, and P6-thread-api.md - implementation design records.
- P7-gc-design.md, P7-gil-removal.md, and P8-structs.md - GC, no-GIL, and TC39 structs planning.