UNPKG

async-rwlock

Version:

Promise-based asynchronous readers-writers lock

132 lines 4.57 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); var State; (function (State) { State[State["Idle"] = 0] = "Idle"; State[State["Reading"] = 1] = "Reading"; State[State["Writing"] = 2] = "Writing"; })(State = exports.State || (exports.State = {})); exports.ErrNotLocked = new Error('rwlock is already unlocked'); exports.ErrTimeout = new Error('acquire lock timeout'); class RWLock { constructor() { this.pendingReaders = []; this.pendingWriters = []; this.numReaders = 0; this.state = State.Idle; this.getState = this.getState.bind(this); this.readLock = this.readLock.bind(this); this.writeLock = this.writeLock.bind(this); this.unlock = this.unlock.bind(this); } getState() { return this.state; } readLock(timeout = Infinity) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { if (this.state === State.Writing) { this.pend(this.pendingReaders, resolve, reject, timeout); } else { this.numReaders++; this.state = State.Reading; resolve(); } }); }); } writeLock(timeout = Infinity) { return __awaiter(this, void 0, void 0, function* () { return new Promise((resolve, reject) => { if (this.state !== State.Idle) { this.pend(this.pendingWriters, resolve, reject, timeout); } else { this.state = State.Writing; resolve(); } }); }); } unlock() { switch (this.state) { case State.Reading: this.numReaders--; if (this.numReaders) { return; } if (this.pendingWriters.length) { this.allowWrite(); } else { this.state = State.Idle; } return; case State.Writing: const nReaders = this.pendingReaders.length; const nWriters = this.pendingWriters.length; if (nReaders && nWriters) { // prevent starving by randomly selecting reader/writer Math.random() >= 0.5 ? this.allowRead() : this.allowWrite(); } else if (nReaders) { this.allowRead(); } else if (nWriters) { this.allowWrite(); } else { this.state = State.Idle; } return; default: throw exports.ErrNotLocked; } } pend(queue, resolve, reject, timeout) { let timer = null; let rejected = false; const doResolve = () => { if (rejected) { return; } if (timer) { clearTimeout(timer); } resolve(); }; const doReject = () => { rejected = true; reject(exports.ErrTimeout); }; if (timeout && timeout >= 0 && timeout < Infinity) { timer = setTimeout(doReject, timeout); } queue.push(doResolve); } allowWrite() { const writer = this.pendingWriters.shift(); this.state = State.Writing; writer(); } allowRead() { const readers = this.pendingReaders.slice(); this.numReaders = readers.length; this.pendingReaders = []; this.state = State.Reading; for (const reader of readers) { reader(); } } } exports.RWLock = RWLock; //# sourceMappingURL=rwlock.js.map