multithreading
Version:
The missing standard library for multithreading in JavaScript (Works in the browser, Node.js, Deno, Bun).
127 lines (126 loc) • 7.2 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _Barrier_instances, _a, _Barrier_state, _Barrier_lock, _Barrier_unlock, _Barrier_lockAsync;
import { register, Serializable, toDeserialized, toSerialized, } from "../shared.js";
const IDX_LOCK = 0;
const IDX_CAP = 1; // Capacity (N)
const IDX_COUNT = 2; // Current count (Starts at 0, goes to N, or N down to 0)
const IDX_GEN = 3; // Generation ID
const META_SIZE = 4;
const LOCK_UNLOCKED = 0;
const LOCK_LOCKED = 1;
export class Barrier extends Serializable {
constructor(n, _buffer) {
super();
_Barrier_instances.add(this);
_Barrier_state.set(this, void 0);
if (_buffer) {
__classPrivateFieldSet(this, _Barrier_state, new Int32Array(_buffer), "f");
}
else {
if (n === undefined) {
throw new Error("Barrier capacity must be provided");
}
__classPrivateFieldSet(this, _Barrier_state, new Int32Array(new SharedArrayBuffer(META_SIZE * Int32Array.BYTES_PER_ELEMENT)), "f");
__classPrivateFieldGet(this, _Barrier_state, "f")[IDX_LOCK] = LOCK_UNLOCKED;
__classPrivateFieldGet(this, _Barrier_state, "f")[IDX_CAP] = n;
__classPrivateFieldGet(this, _Barrier_state, "f")[IDX_COUNT] = n; // We count down from N to 0
__classPrivateFieldGet(this, _Barrier_state, "f")[IDX_GEN] = 0;
}
}
/**
* Blocks the current thread until all participating threads have reached the barrier.
*/
blockingWait() {
__classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_lock).call(this);
const localGen = Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN);
const count = Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT) - 1;
if (count === 0) {
// We are the leader (the last one to arrive)
Atomics.store(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT, Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_CAP));
Atomics.add(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, 1);
__classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_unlock).call(this);
// Wake everyone up. They are waiting on IDX_GEN changing.
Atomics.notify(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, Infinity);
return { isLeader: true };
}
else {
// We are a follower
Atomics.store(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT, count);
__classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_unlock).call(this);
// Wait until the generation changes
while (Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN) === localGen) {
Atomics.wait(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, localGen);
}
return { isLeader: false };
}
}
/**
* Asynchronously waits until all participating threads have reached the barrier.
*/
async wait() {
await __classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_lockAsync).call(this);
const localGen = Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN);
const count = Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT) - 1;
if (count === 0) {
// We are the leader
Atomics.store(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT, Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_CAP));
Atomics.add(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, 1);
__classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_unlock).call(this);
Atomics.notify(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, Infinity);
return { isLeader: true };
}
else {
// We are a follower
Atomics.store(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_COUNT, count);
__classPrivateFieldGet(this, _Barrier_instances, "m", _Barrier_unlock).call(this);
// Wait until the generation changes
while (Atomics.load(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN) === localGen) {
const res = Atomics.waitAsync(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_GEN, localGen);
if (res.async) {
await res.value;
}
}
return { isLeader: false };
}
}
[(_Barrier_state = new WeakMap(), _Barrier_instances = new WeakSet(), _Barrier_lock = function _Barrier_lock() {
while (Atomics.compareExchange(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, LOCK_UNLOCKED, LOCK_LOCKED) !== LOCK_UNLOCKED) {
Atomics.wait(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, LOCK_LOCKED);
}
}, _Barrier_unlock = function _Barrier_unlock() {
if (Atomics.compareExchange(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, LOCK_LOCKED, LOCK_UNLOCKED) !== LOCK_LOCKED) {
throw new Error("Barrier lock state corrupted");
}
Atomics.notify(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, 1);
}, _Barrier_lockAsync =
/**
* Async version of the internal lock for Main Thread compatibility.
*/
async function _Barrier_lockAsync() {
while (Atomics.compareExchange(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, LOCK_UNLOCKED, LOCK_LOCKED) !== LOCK_UNLOCKED) {
const res = Atomics.waitAsync(__classPrivateFieldGet(this, _Barrier_state, "f"), IDX_LOCK, LOCK_LOCKED);
if (res.async) {
await res.value;
}
}
}, toSerialized)]() {
return [__classPrivateFieldGet(this, _Barrier_state, "f").buffer];
}
static [toDeserialized](buffer) {
return new _a(undefined, buffer);
}
}
_a = Barrier;
(() => {
register(8, _a);
})();