async-rwlock
Version:
Promise-based asynchronous readers-writers lock
132 lines • 4.57 kB
JavaScript
"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