12 views
worker_threads give you real parallelism for CPU-bound work. SharedArrayBuffer gives you a way to corrupt state at nanosecond speed if you invent a protocol casually. The senior pattern is boring: one writer owns each region, Atomics coordinate handoff, and ordinary JS objects never cross the boundary except as structured-clone messages for control plane events.
⚡ TL;DR: Use SAB for numeric payloads and ring buffers only; gate every multi-byte write withAtomics(or transfer ownership so only one thread touches a slice); keep request metadata onMessagePort; fuzz your protocol undernode --testwith thread sanitizers in CI where available. Pair with AI Test Generation: Property Tests That Catch Race Conditions for concurrency oracles.
Node Worker Threads: production guidance
If two threads can write the same index without a happens-before edge, you do not have a protocol — you have a race. Prefer single-producer/single-consumer rings.typescript
// src/sab-ring.ts
const HEAD = 0; // Int32 index in control SAB
const TAIL = 1;
export function createRing(slots: number, slotBytes: number) {
const control = new SharedArrayBuffer(8); // 2 * int32
const data = new SharedArrayBuffer(slots * slotBytes);
const ctrl = new Int32Array(control);
Atomics.store(ctrl, HEAD, 0);
Atomics.store(ctrl, TAIL, 0);
return { control, data, slots, slotBytes };
}
export function tryPush(ctrl: Int32Array, data: Uint8Array, slots: number, slotBytes: number, payload: Uint8Array) {
const head = Atomics.load(ctrl, HEAD);
const tail = Atomics.load(ctrl, TAIL);
if ((head + 1) % slots === tail) return false; // full
data.set(payload, head * slotBytes);
// ✅ Publish payload before advancing head
Atomics.store(ctrl, HEAD, (head + 1) % slots);
Atomics.notify(ctrl, HEAD, 1);
return true;
}
export function tryPop(ctrl: Int32Array, data: Uint8Array, slots: number, slotBytes: number, out: Uint8Array) {
const head = Atomics.load(ctrl, HEAD);
const tail = Atomics.load(ctrl, TAIL);
if (head === tail) return false;
out.set(data.subarray(tail * slotBytes, tail * slotBytes + out.length));
Atomics.store(ctrl, TAIL, (tail + 1) % slots);
return true;
}
typescript
// ❌ Shared mutable object via workerData — not shared, not safe if you "fix" it with SAB tricks
workerData: { state: { counters: {} } }
Control plane on MessagePort, data plane on SAB
Config updates, cancel tokens, and error objects belong on the message channel. Bytes and floats belong on SAB.typescript
// src/cpu-pool.ts
import { Worker } from "node:worker_threads";
import { createRing } from "./sab-ring.js";
export function startPool(n: number) {
const ring = createRing(1024, 64);
const workers = Array.from({ length: n }, () => {
const w = new Worker(new URL("./cpu-worker.js", import.meta.url), {
workerData: { control: ring.control, data: ring.data, slots: ring.slots, slotBytes: ring.slotBytes },
});
w.on("message", (msg) => {
if (msg.type === "fatal") metrics.increment("pool.worker_fatal");
});
return w;
});
return { ring, workers };
}
Atomics wait without wedging the event loop
Atomics.wait blocks the worker thread — that is fine on workers, catastrophic on the main thread. On the main thread use Atomics.waitAsync (or message notifications) so you do not recreate the stalls covered in Node.js Event Loop Lag: Catch P99 Stalls.
typescript
// cpu-worker.ts
import { workerData, parentPort } from "node:worker_threads";
const ctrl = new Int32Array(workerData.control);
const data = new Uint8Array(workerData.data);
while (true) {
// ✅ Block only inside the worker
while (!tryConsume()) {
Atomics.wait(ctrl, 0 /* HEAD */, Atomics.load(ctrl, 0));
}
}
function tryConsume() {
// pop + compute + optional MessagePort result for large JS values
return false;
}
Testing races on purpose
Deterministic seeds + forced interleaving beat “it passed on my laptop.”typescript
import { test } from "node:test";
import assert from "node:assert/strict";
test("ring never duplicates slots under contention", async () => {
// spawn 1 producer + 4 consumers, push N items, assert set size === N
assert.equal(1, 1); // replace with real harness
});
Closing checklist
✅ Dos– ✅ Single-writer regions with Atomics publish order
– ✅ MessagePort for objects/errors/cancel
– ✅
Atomics.wait only on worker threads– ✅ Fuzz fill/empty boundaries and wraparound
– ✅ Document endianness and slot layout in one ADR ❌ Don’ts
– ❌ Don’t share ordinary JS objects across threads
– ❌ Don’t use non-atomic multi-byte reads/writes on SAB
– ❌ Don’t
Atomics.wait on the main thread– ❌ Don’t grow SAB protocols without a version field
– ❌ Don’t ignore worker
error / exit events
Related reading
- AI Test Generation: Property Tests That Catch Race Conditions
- Node.js Event Loop Lag: Catch P99 Stalls
- AST-Guided Edits: LLMs Propose Intents and Codemods Apply Them
- OpenTelemetry for LLMs: Trace Prompt Latency Across Microservices
Watch: 100 Days of AI on YouTube
Short videos from CheatCoders, one AI engineering topic per day.
Most viewed
- Python Decorators Explained: From Simple Wrappers to Production Patterns
- Claude Code vs Cursor vs GitHub Copilot: Choose by Workflow, and Set Up One Repo That Works With All Three
- AI Agent Frameworks in 2025: LangGraph vs CrewAI vs AutoGen vs Raw API
- Python String Methods: Every str Method With Real Production Examples
- Css Animation Performance: Performance-Optimized Motion for Modern UIs
Newly added
- AWS Systems Manager Parameter Store: Hierarchical Runtime Config for Coding Agents
- Amazon Cognito Identity Pools: Federate Coding-Agent Tool Callers Without Long-Lived Keys
- Amazon Athena for Coding Agents: Query Tool Audit Logs and Eval Traces on S3 Without a Warehouse
- AWS Batch for Coding Agents: Parallel Overnight PR Reviews and Long-Running Jobs Without Lambda Timeouts
- Amazon Bedrock Prompt Management: Version and A/B Test Coding-Agent System Prompts
Deep-dive PDF
Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.
Already subscribed? or open the subscribe page.
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
