SharedArrayBuffer and Atomics in JavaScript Concurrency
JavaScript is traditionally single-threaded, relying on an event loop
and message-passing Web Workers for asynchronous execution. However,
true shared-memory concurrency is possible using
SharedArrayBuffer and the Atomics object.
SharedArrayBuffer allows multiple Web Workers and the main
thread to read and write to the exact same memory space without data
copying, while Atomics provides the synchronization
primitives and thread-safe operations necessary to prevent race
conditions and manage thread execution safely.
The Role of SharedArrayBuffer
Standard Web Workers communicate by passing messages via
postMessage(), which uses the structured clone algorithm.
This process serializes and copies data between threads, introducing
memory overhead and latency for large datasets.
SharedArrayBuffer eliminates this overhead by allocating
a shared chunk of raw binary memory. Instead of copying data:
- A
SharedArrayBufferinstance is created on one thread (typically the main thread or a coordinator worker). - The buffer is shared with other Web Workers via
postMessage(). - Workers wrap the shared buffer in a typed array (such as
Int32ArrayorUint8Array). - All threads can directly read and write to the identical memory addresses simultaneously.
While this allows zero-copy data sharing and high-throughput parallel processing, concurrent writes to the same memory addresses introduce critical concurrency bugs, including data races and torn reads/writes.
Preventing Race Conditions with the Atomics Object
The global Atomics object provides static methods that
guarantee operations on shared memory are performed sequentially and
without interruption from other threads.
Atomic Operations
Standard JavaScript arithmetic operations (like
array[0]++) are not atomic; they involve reading,
modifying, and writing back the value in separate steps. If two threads
execute this simultaneously, updates can be lost.
Atomics offers atomic mathematical and logical
operations that execute as a single, uninterruptible instruction at the
hardware level:
Atomics.add(typedArray, index, value)/Atomics.sub(...): Safely increments or decrements a value at a specific index.Atomics.load(typedArray, index): Guarantees a fresh, synchronized read from memory without CPU-level caching anomalies.Atomics.store(typedArray, index, value): Guarantees that a write is immediately visible to all other threads.Atomics.exchange(typedArray, index, value): Stores a value and returns the old value in one atomic step.Atomics.compareExchange(typedArray, index, expectedValue, replacementValue): Replaces a value only if it matches an expected value, forming the foundation of non-blocking algorithms and lock systems.
Thread Synchronization and Signaling
Beyond basic memory manipulation, Atomics allows threads
to coordinate and manage execution state without burning CPU resources
in busy-wait loops:
Atomics.wait(typedArray, index, expectedValue[, timeout]): Suspends the calling worker thread if the value at the given index matchesexpectedValue. The thread sleeps until it is notified or times out. (Note:Atomics.waitcannot be called on the browser’s main UI thread to prevent interface freezing).Atomics.notify(typedArray, index[, count]): Wakes up a specified number of sleeping threads that were paused viaAtomics.waiton that memory index.Atomics.waitAsync(typedArray, index, expectedValue[, timeout]): A non-blocking variant that returns aPromise, making safe synchronization available on the main thread.
These signaling methods make it possible to implement higher-level synchronization primitives such as mutexes, semaphores, spinlocks, and barrier synchronization directly in JavaScript and WebAssembly.
Primary Use Cases and Security Requirements
Shared-memory concurrency is crucial for performance-intensive applications running in modern browsers:
- WebAssembly (Wasm) Multithreading: Wasm compiles
multithreaded C/C++ or Rust code directly to the web, relying on
SharedArrayBufferto emulate POSIX threads (pthreads). - Game Engines and Physics: Performing complex physics simulations, spatial partitioning, or matrix calculations in parallel without serialization bottlenecks.
- Audio and Video Processing: Real-time manipulation of media streams within dedicated worker threads.
Due to security mitigations against speculative execution
side-channel attacks (like Spectre), browsers require web pages using
SharedArrayBuffer to be served in a cross-origin isolated
environment by defining specific HTTP headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
By pairing SharedArrayBuffer for zero-copy data access
with Atomics for thread synchronization, JavaScript
provides a robust, low-level architecture for high-performance
multithreaded computing.