UNPKG

multithreading

Version:

The missing standard library for multithreading in JavaScript (Works in the browser, Node.js, Deno, Bun).

180 lines (179 loc) 9.97 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 _RwLockReadGuard_data, _RwLockReadGuard_controller, _RwLockReadGuard_released, _RwLockWriteGuard_data, _RwLockWriteGuard_controller, _RwLockWriteGuard_released, _RwLock_instances, _a, _RwLock_data, _RwLock_lockState, _RwLock_readController, _RwLock_writeController, _RwLock_unlockRead, _RwLock_unlockWrite; import { deserialize, register, Serializable, serialize, toDeserialized, toSerialized, } from "../shared.js"; export const INTERNAL_RWLOCK_CONTROLLER = Symbol("Thread.InternalRwLockController"); const IDX_LOCK_STATE = 0; const UNLOCKED = 0; const WRITE_LOCKED = -1; const READ_ONE = 1; export class RwLockReadGuard { constructor(data, controller) { _RwLockReadGuard_data.set(this, void 0); _RwLockReadGuard_controller.set(this, void 0); _RwLockReadGuard_released.set(this, false); __classPrivateFieldSet(this, _RwLockReadGuard_data, data, "f"); __classPrivateFieldSet(this, _RwLockReadGuard_controller, controller, "f"); } get [(_RwLockReadGuard_data = new WeakMap(), _RwLockReadGuard_controller = new WeakMap(), _RwLockReadGuard_released = new WeakMap(), INTERNAL_RWLOCK_CONTROLLER)]() { return __classPrivateFieldGet(this, _RwLockReadGuard_controller, "f"); } get value() { if (__classPrivateFieldGet(this, _RwLockReadGuard_released, "f")) throw new Error("Cannot access released lock data"); return __classPrivateFieldGet(this, _RwLockReadGuard_data, "f"); } [Symbol.dispose]() { if (!__classPrivateFieldGet(this, _RwLockReadGuard_released, "f")) { __classPrivateFieldSet(this, _RwLockReadGuard_released, true, "f"); __classPrivateFieldGet(this, _RwLockReadGuard_controller, "f").unlock(); } } dispose() { this[Symbol.dispose](); } } export class RwLockWriteGuard { constructor(data, controller) { _RwLockWriteGuard_data.set(this, void 0); _RwLockWriteGuard_controller.set(this, void 0); _RwLockWriteGuard_released.set(this, false); __classPrivateFieldSet(this, _RwLockWriteGuard_data, data, "f"); __classPrivateFieldSet(this, _RwLockWriteGuard_controller, controller, "f"); } get [(_RwLockWriteGuard_data = new WeakMap(), _RwLockWriteGuard_controller = new WeakMap(), _RwLockWriteGuard_released = new WeakMap(), INTERNAL_RWLOCK_CONTROLLER)]() { return __classPrivateFieldGet(this, _RwLockWriteGuard_controller, "f"); } get value() { if (__classPrivateFieldGet(this, _RwLockWriteGuard_released, "f")) throw new Error("Cannot access released lock data"); return __classPrivateFieldGet(this, _RwLockWriteGuard_data, "f"); } [Symbol.dispose]() { if (!__classPrivateFieldGet(this, _RwLockWriteGuard_released, "f")) { __classPrivateFieldSet(this, _RwLockWriteGuard_released, true, "f"); __classPrivateFieldGet(this, _RwLockWriteGuard_controller, "f").unlock(); } } dispose() { this[Symbol.dispose](); } } export class RwLock extends Serializable { constructor(data, _stateBuffer) { super(); _RwLock_instances.add(this); _RwLock_data.set(this, void 0); _RwLock_lockState.set(this, void 0); _RwLock_readController.set(this, void 0); _RwLock_writeController.set(this, void 0); __classPrivateFieldSet(this, _RwLock_data, data, "f"); __classPrivateFieldSet(this, _RwLock_lockState, _stateBuffer ? new Int32Array(_stateBuffer) : new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)), "f"); __classPrivateFieldSet(this, _RwLock_readController, { unlock: () => __classPrivateFieldGet(this, _RwLock_instances, "m", _RwLock_unlockRead).call(this) }, "f"); __classPrivateFieldSet(this, _RwLock_writeController, { unlock: () => __classPrivateFieldGet(this, _RwLock_instances, "m", _RwLock_unlockWrite).call(this) }, "f"); } // --- Public API --- blockingRead() { while (true) { const current = Atomics.load(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE); // If write locked (current == -1), wait. if (current === WRITE_LOCKED) { Atomics.wait(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, WRITE_LOCKED); continue; } // Optimistic increment if (Atomics.compareExchange(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, current, current + READ_ONE) === current) { return new RwLockReadGuard(__classPrivateFieldGet(this, _RwLock_data, "f"), __classPrivateFieldGet(this, _RwLock_readController, "f")); } } } async read() { while (true) { const current = Atomics.load(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE); if (current === WRITE_LOCKED) { const res = Atomics.waitAsync(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, WRITE_LOCKED); if (res.async) { await res.value; } continue; } if (Atomics.compareExchange(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, current, current + READ_ONE) === current) { return new RwLockReadGuard(__classPrivateFieldGet(this, _RwLock_data, "f"), __classPrivateFieldGet(this, _RwLock_readController, "f")); } } } blockingWrite() { while (true) { const current = Atomics.load(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE); // Can only write if strictly UNLOCKED (0). if (current !== UNLOCKED) { Atomics.wait(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, current); continue; } if (Atomics.compareExchange(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, UNLOCKED, WRITE_LOCKED) === UNLOCKED) { return new RwLockWriteGuard(__classPrivateFieldGet(this, _RwLock_data, "f"), __classPrivateFieldGet(this, _RwLock_writeController, "f")); } } } async write() { while (true) { const current = Atomics.load(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE); if (current !== UNLOCKED) { const res = Atomics.waitAsync(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, current); if (res.async) { await res.value; } continue; } if (Atomics.compareExchange(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, UNLOCKED, WRITE_LOCKED) === UNLOCKED) { return new RwLockWriteGuard(__classPrivateFieldGet(this, _RwLock_data, "f"), __classPrivateFieldGet(this, _RwLock_writeController, "f")); } } } [(_RwLock_data = new WeakMap(), _RwLock_lockState = new WeakMap(), _RwLock_readController = new WeakMap(), _RwLock_writeController = new WeakMap(), _RwLock_instances = new WeakSet(), _RwLock_unlockRead = function _RwLock_unlockRead() { const prevState = Atomics.sub(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, READ_ONE); // If we were the last reader (prevState was 1, now 0), notify writers if (prevState === READ_ONE) { Atomics.notify(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, 1); } }, _RwLock_unlockWrite = function _RwLock_unlockWrite() { if (Atomics.compareExchange(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, WRITE_LOCKED, UNLOCKED) !== WRITE_LOCKED) { throw new Error("RwLock was not write-locked or locked by another thread"); } // Notify all waiting readers or one waiting writer. // We use Infinity because we might have N readers waiting. Atomics.notify(__classPrivateFieldGet(this, _RwLock_lockState, "f"), IDX_LOCK_STATE, Infinity); }, toSerialized)]() { let serializedData; let transfer = []; if (__classPrivateFieldGet(this, _RwLock_data, "f") !== undefined) { [serializedData, transfer] = serialize(__classPrivateFieldGet(this, _RwLock_data, "f")); } return [ [__classPrivateFieldGet(this, _RwLock_lockState, "f").buffer, serializedData], transfer, ]; } static [toDeserialized](obj) { const data = obj[1] !== undefined ? deserialize(obj[1]) : undefined; return new _a(data, obj[0]); } } _a = RwLock; (() => { register(2, _a); })();