UNPKG

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
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); })();