@apiratorjs/locking
Version:
A lightweight library providing both local and distributed locking primitives (mutexes, semaphores, and read-write locks) for managing concurrency in Node.js.
106 lines • 3.92 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadWriteLock = void 0;
const semaphore_1 = require("./semaphore");
/**
* ReadWriteLock (short for Read-Write Lock) is a synchronization mechanism that allows multiple threads to read from a resource simultaneously,
* but prohibits writing if someone is reading, and vice versa: if writing is in progress, no one can read or write.
*
* The main principles of ReadWriteLock operation:
* • Multiple threads can read simultaneously if there is no writing.
* • Only one thread can write, and during writing, no reads are allowed.
* • This improves performance with a large number of read operations and infrequent writes.
*
* This implementation handles locks in the order they are requested:
* • Readers can acquire locks concurrently as long as no writer is active
* • Writers must wait for all active readers to release their locks before acquiring the write lock
* • New readers can acquire locks even if writers are waiting, as long as no writer is currently active
*
* Explanation in simple terms:
* 1. There are multiple threads reading data — that's fine, they can do it simultaneously.
* 2. At some point, a thread wants to write — it calls acquireWrite():
* • It cannot write while others are reading, so it queues and waits.
* 3. Meanwhile, new read requests can still be granted while writers are waiting.
*/
class ReadWriteLock {
constructor(props) {
this._readSemaphore = new semaphore_1.Semaphore(props?.maxReaders || 100);
this._writeSemaphore = new semaphore_1.Semaphore(1);
}
async maxReaders() {
return this._readSemaphore.maxCount;
}
async activeReaders() {
const freeCount = await this._readSemaphore.freeCount();
const maxCount = this._readSemaphore.maxCount;
return maxCount - freeCount;
}
async acquireRead(params) {
await this._writeSemaphore.waitForFullyUnlock();
const token = `rwlock:read:${crypto.randomUUID()}`;
const readReleaser = await this._readSemaphore.acquire(params, token);
return {
release: () => readReleaser.release(),
getToken: () => token
};
}
async acquireWrite(params) {
await this._readSemaphore.waitForFullyUnlock();
const token = `rwlock:write:${crypto.randomUUID()}`;
const writeReleaser = await this._writeSemaphore.acquire(params, token);
return {
release: () => writeReleaser.release(),
getToken: () => token
};
}
async cancelAll(errMessage) {
await Promise.all([
this._readSemaphore.cancelAll(errMessage),
this._writeSemaphore.cancelAll(errMessage)
]);
}
async withReadLock(...args) {
let callback;
let params;
if (args.length === 1) {
callback = args[0];
}
else {
params = args[0];
callback = args[1];
}
const releaser = await this.acquireRead(params);
try {
return await callback();
}
finally {
await releaser.release();
}
}
async withWriteLock(...args) {
let callback;
let params;
if (args.length === 1) {
callback = args[0];
}
else {
params = args[0];
callback = args[1];
}
const releaser = await this.acquireWrite(params);
try {
return await callback();
}
finally {
await releaser.release();
}
}
async isWriteLocked() {
return this._writeSemaphore.isLocked();
}
async isReadLocked() {
return this._readSemaphore.isLocked();
}
}
exports.ReadWriteLock = ReadWriteLock;
//# sourceMappingURL=read-write-lock.js.map