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