import { LockCallable } from "../types";
/**
* @description Base lock implementation for concurrency control
* @summary Provides a basic lock mechanism for controlling access to shared resources, with support for queuing and executing functions when the lock is available
* @class Lock
* @example
* // Using the Lock class to execute a function with exclusive access
* const lock = new Lock();
* const result = await lock.execute(async () => {
* // This code will run with exclusive access
* return await performCriticalOperation();
* });
*/
export class Lock {
private queue: LockCallable[] = [];
private locked = false;
/**
* @description Executes a function with exclusive lock access
* @summary Acquires the lock, executes the provided function, and releases the lock afterward, ensuring proper cleanup even if the function throws an error
* @param {Function} func - The function to execute when the lock is acquired
* @return {Promise<any>} A promise that resolves with the result of the executed function
*/
async execute(func: () => any) {
await this.acquire();
let result: any;
try {
result = await Promise.resolve(func());
} catch (e: any) {
this.release();
throw e;
}
this.release();
return result;
}
/**
* @summary waits to acquire the lock
* @param {string} [issuer]
*/
async acquire(): Promise<void> {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
if (self.locked) {
return new Promise<void>((resolve) => self.queue.push(resolve));
} else {
self.locked = true;
return Promise.resolve();
}
}
/**
* @summary releases the lock
*/
release() {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const self = this;
const next: LockCallable | undefined = self.queue.shift();
if (next) {
if (
typeof (globalThis as unknown as { window: any }).window === "undefined"
)
globalThis.process.nextTick(next); // if you are on node
else setTimeout(next, 0); // if you are in the browser
} else {
self.locked = false;
}
}
}
Source