atomics-sync
v1.0.3
Published
JavaScript multithreading synchronization library
Downloads
8
Maintainers
Readme
Atomics Sync
Atomics Sync is lightweight library providing thread-safe synchronization primitives for JavaScript environments with shared memory support (Web Workers, Node.js worker_threads). Implements essential concurrency control mechanisms using SharedArrayBuffer and Atomics API.
Features
- Mutex - Mutual exclusion lock for critical sections
- SpinLock - Low-level busy-wait lock for very short operations
- Semaphore - Counting semaphore for resource management
- Condition - Condition variables for thread signaling
- Barrier - Synchronization point for multiple threads
- Once - One-time initialization primitive
Important: For browsers, your server must send these headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpInstallation
npm install atomics-syncWhy This Library?
Modern JavaScript applications increasingly use:
- Web Workers for parallel processing
- SharedArrayBuffer for shared memory
- CPU-intensive tasks (WASM, WebGL, etc.)
These primitives help coordinate work between threads while preventing:
- Race conditions
- Data corruption
- Deadlocks
Usage Examples
Important: There is no reliable way for a thread to know its own ID automatically in JavaScript environments. The parent/main thread must explicitly assign and pass a unique thread ID to each worker thread it creates.
Mutex
Init mutex to work safely with shared data:
const shared = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)
);
const mutex = Mutex.init();Pass mutex and shared to threads. Remember about threadId:
const worker = new Worker("./worker.js", {
workerData: { threadId, shared, mutex }
});Within thread use lock/unlock methods to wrap critical section:
try {
Mutex.lock(mutex, threadId);
// work with shared data here
} finally {
Mutex.unlock(mutex, threadId);
}See full example.
Semaphore
Here's a practical example demonstrating how to use a semaphore to make one thread wait for another thread to complete certain actions:
Init semaphore:
const sem = Semaphore.init(0);One thread creates another thread and must wait some initialization actions within it:
new Worker("./worker.js", { workerData: { sem } });
Semaphore.wait(sem);
// continue execution
// ...Created thread performs necessary operations and notify parent thread:
// ...
initSomeImportantThings();
Semaphore.post(sem);See full example.
Condition
Using a condition variable, we can make one thread wait for a change in a shared variable (protected by a mutex) before proceeding with its operation.
Init condition variable and mutex, allocate shared variable:
const cond = Condition.init();
const mtx = Mutex.init();
const shared = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)
);
shared[0] = -1;One thread produces value:
Mutex.lock(mtx, threadId);
shared[0] = Math.floor(Math.random() * 10);
Condition.signal(cond);
Mutex.unlock(mtx, threadId);Another thread consumes the value and makes some work with it:
Mutex.lock(mtx, threadId);
while (shared[0] < 0) {
Condition.wait(cond, mtx, threadId);
}
shared[0] *= 10;
Mutex.unlock(mtx, threadId);See full example.
SpinLock
A spinlock provides an interface nearly identical to a mutex (lock()/unlock()), but is optimized for very short wait times where spinning (busy-waiting) is more efficient than thread suspension.
Barrier
A barrier synchronizes multiple threads at a specific execution point.
In this example, we launch 10 threads that execute at variable speeds and create a barrier with a count of 5.
// main.js
const barrier = Barrier.init(5);
for (let i = 0; i < 10; i++) {
const threadId = i + 1;
const worker = new Worker("./worker.js", {
workerData: { threadId, barrier }
});
}
// worker.js
setTimeout(() => {
// ...
Barrier.wait(barrier, threadId);
// ...
}, threadId * 100);The first 5 threads to reach the barrier will block and wait.
Once the 5th thread arrives, the barrier releases all waiting threads.
The remaining 5 threads then proceed through the barrier in the same way.
See full example.
Once
A Once primitive ensures one-time initialization in concurrent environments.
Init once value:
const once = Once.init();Pass it into some threads:
const worker = new Worker("./worker.js", {
workerData: { once }
});Within thread:
Once.execute(once, () => {
// some logic that should be executed only once
});See full example.
Documentation
For complete API reference, see API documentation.
