node-json-db
Version:
Database using JSON file as storage for Node.JS
281 lines • 9.38 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ReadWriteLock = exports.LockType = void 0;
const Error_1 = require("./Error");
/**
* Lock type enumeration
*/
var LockType;
(function (LockType) {
LockType["READ"] = "read";
LockType["WRITE"] = "write";
})(LockType || (exports.LockType = LockType = {}));
/**
* ReadWriteLock implementation with high-performance optimizations
* Features fast path for uncontended locks and object pooling
*/
class ReadWriteLock {
readers = 0;
writer = false;
queue = [];
maxReaders = Infinity;
// Object pools for performance optimization
readReleasePool = [];
writeReleasePool = [];
requestPool = [];
/**
* Acquire a read lock with fast path optimization
* @param timeout Optional timeout in milliseconds
* @returns Promise that resolves when lock is acquired, or synchronous result for fast path
*/
async readLock(timeout) {
// Fast path: Check if we can acquire immediately without contention
if (!this.writer &&
this.queue.length === 0 &&
this.readers < this.maxReaders) {
// Synchronous fast path - no Promise overhead
this.readers++;
return this.getPooledReadRelease();
}
// Slow path: Need to queue the request
return new Promise((resolve, reject) => {
const request = this.getPooledRequest();
request.type = LockType.READ;
request.resolve = () => {
this.readers++;
resolve(this.getPooledReadRelease());
};
request.reject = reject;
request.timeout = timeout;
request.timeoutId = undefined;
if (timeout !== undefined) {
request.timeoutId = setTimeout(() => {
this.removeFromQueue(request);
this.returnRequestToPool(request);
reject(new Error_1.TimeoutError("Read lock timeout", 100));
}, timeout);
}
this.queue.push(request);
this.processQueue();
});
}
/**
* Acquire a write lock with fast path optimization
* @param timeout Optional timeout in milliseconds
* @returns Promise that resolves when lock is acquired, or synchronous result for fast path
*/
async writeLock(timeout) {
// Fast path: Check if we can acquire immediately without contention
if (this.readers === 0 && !this.writer && this.queue.length === 0) {
// Synchronous fast path - no Promise overhead
this.writer = true;
return this.getPooledWriteRelease();
}
// Slow path: Need to queue the request
return new Promise((resolve, reject) => {
const request = this.getPooledRequest();
request.type = LockType.WRITE;
request.resolve = () => {
this.writer = true;
resolve(this.getPooledWriteRelease());
};
request.reject = reject;
request.timeout = timeout;
request.timeoutId = undefined;
if (timeout !== undefined) {
request.timeoutId = setTimeout(() => {
this.removeFromQueue(request);
this.returnRequestToPool(request);
reject(new Error_1.TimeoutError("Write lock timeout", 100));
}, timeout);
}
this.queue.push(request);
this.processQueue();
});
}
/**
* Get a pooled read release function
*/
getPooledReadRelease() {
let releaseFunc = this.readReleasePool.pop();
if (!releaseFunc) {
releaseFunc = () => {
this.readers = Math.max(0, this.readers - 1);
this.processQueue();
// Return to pool for reuse
if (releaseFunc && releaseFunc._isPooled) {
this.readReleasePool.push(releaseFunc);
}
};
releaseFunc._isPooled = true;
releaseFunc._lockInstance = this;
releaseFunc._lockType = LockType.READ;
}
else {
// Reset the function's closure context
releaseFunc._lockInstance = this;
releaseFunc._lockType = LockType.READ;
}
return releaseFunc;
}
/**
* Get a pooled write release function
*/
getPooledWriteRelease() {
let releaseFunc = this.writeReleasePool.pop();
if (!releaseFunc) {
releaseFunc = () => {
this.writer = false;
this.processQueue();
// Return to pool for reuse
if (releaseFunc && releaseFunc._isPooled) {
this.writeReleasePool.push(releaseFunc);
}
};
releaseFunc._isPooled = true;
releaseFunc._lockInstance = this;
releaseFunc._lockType = LockType.WRITE;
}
else {
// Reset the function's closure context
releaseFunc._lockInstance = this;
releaseFunc._lockType = LockType.WRITE;
}
return releaseFunc;
}
/**
* Get a pooled request object
*/
getPooledRequest() {
const request = this.requestPool.pop();
if (request) {
// Reset the request object
request.timeoutId = undefined;
return request;
}
// Create new request if pool is empty
return {
type: LockType.READ, // Will be overwritten
resolve: () => { }, // Will be overwritten
reject: () => { }, // Will be overwritten
};
}
/**
* Return a request object to the pool
*/
returnRequestToPool(request) {
// Clear any timeout
if (request.timeoutId) {
clearTimeout(request.timeoutId);
request.timeoutId = undefined;
}
// Reset properties to avoid memory leaks
request.resolve = () => { };
request.reject = () => { };
request.timeout = undefined;
// Return to pool if not too large
if (this.requestPool.length < 10) {
this.requestPool.push(request);
}
}
/**
* Process the lock request queue with optimizations
*/
processQueue() {
while (this.queue.length > 0) {
const request = this.queue[0];
// Check if we can grant the lock
if (this.canGrantLock(request)) {
// Remove from queue and clear timeout
this.queue.shift();
if (request.timeoutId) {
clearTimeout(request.timeoutId);
}
// Grant the lock
request.resolve();
// Return request to pool
this.returnRequestToPool(request);
}
else {
// Can't grant this lock, stop processing
break;
}
}
}
/**
* Check if a lock request can be granted
*/
canGrantLock(request) {
switch (request.type) {
case LockType.READ:
// Can grant read lock if no writer is active and within max readers limit
return !this.writer && this.readers < this.maxReaders;
case LockType.WRITE:
// Can grant write lock only if no readers or writers are active
return this.readers === 0 && !this.writer;
default:
return false;
}
}
/**
* Remove a request from the queue (used for timeout cleanup)
*/
removeFromQueue(request) {
const index = this.queue.indexOf(request);
if (index !== -1) {
this.queue.splice(index, 1);
}
}
/**
* Get current lock status for debugging
*/
getStatus() {
return {
readers: this.readers,
writer: this.writer,
queueLength: this.queue.length,
};
}
/**
* Set maximum number of concurrent readers
*/
setMaxReaders(max) {
this.maxReaders = max;
}
/**
* Clean up all pending timeouts and clear pools (useful for shutdown)
*/
cleanup() {
// Clear pending timeouts
for (const request of this.queue) {
if (request.timeoutId) {
clearTimeout(request.timeoutId);
}
}
// Clear all state
this.queue.length = 0;
this.readers = 0;
this.writer = false;
// Clear object pools
this.readReleasePool.length = 0;
this.writeReleasePool.length = 0;
this.requestPool.length = 0;
}
/**
* Get performance statistics
*/
getPerformanceStats() {
return {
readers: this.readers,
writer: this.writer,
queueLength: this.queue.length,
poolSizes: {
readRelease: this.readReleasePool.length,
writeRelease: this.writeReleasePool.length,
requests: this.requestPool.length,
},
};
}
}
exports.ReadWriteLock = ReadWriteLock;
//# sourceMappingURL=ReadWriteLock.js.map