UNPKG

@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
"use strict"; 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