X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Futils%2FAsyncLock.ts;h=f6b793a60748beaa8454fdb80e13510553a25898;hb=452a4864d4a8d0286ddd351958d8cc02574b4ba9;hp=53907ce46b723f246aa1bf0361f5986b727caf21;hpb=b9b617a2085713b870c8cfe3a5a65edbac1f8148;p=e-mobility-charging-stations-simulator.git diff --git a/src/utils/AsyncLock.ts b/src/utils/AsyncLock.ts index 53907ce4..f6b793a6 100644 --- a/src/utils/AsyncLock.ts +++ b/src/utils/AsyncLock.ts @@ -1,46 +1,64 @@ -// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved. +// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved. + +import { Queue } from 'mnemonist' + +import { Constants } from './Constants.js' export enum AsyncLockType { configuration = 'configuration', - performance = 'performance', + performance = 'performance' } +type ResolveType = (value: void | PromiseLike) => void + export class AsyncLock { - private static readonly instances = new Map(); - private acquired: boolean; - private readonly resolveQueue: ((value: void | PromiseLike) => void)[]; + private static readonly asyncLocks = new Map() + private acquired: boolean + private readonly resolveQueue: Queue + + private constructor () { + this.acquired = false + this.resolveQueue = new Queue() + } - private constructor(private readonly type: AsyncLockType) { - this.acquired = false; - this.resolveQueue = []; + public static async runExclusive(type: AsyncLockType, fn: () => T | Promise): Promise { + return await AsyncLock.acquire(type) + .then(fn) + .finally(() => { + AsyncLock.release(type).catch(Constants.EMPTY_FUNCTION) + }) } - public static getInstance(type: AsyncLockType): AsyncLock { - if (!AsyncLock.instances.has(type)) { - AsyncLock.instances.set(type, new AsyncLock(type)); + private static async acquire (type: AsyncLockType): Promise { + const asyncLock = AsyncLock.getAsyncLock(type) + if (!asyncLock.acquired) { + asyncLock.acquired = true + return } - return AsyncLock.instances.get(type); + await new Promise(resolve => { + asyncLock.resolveQueue.enqueue(resolve) + }) } - public async acquire(): Promise { - if (!this.acquired) { - this.acquired = true; - } else { - return new Promise((resolve) => { - this.resolveQueue.push(resolve); - }); + private static async release (type: AsyncLockType): Promise { + const asyncLock = AsyncLock.getAsyncLock(type) + if (asyncLock.resolveQueue.size === 0 && asyncLock.acquired) { + asyncLock.acquired = false + return } + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const queuedResolve = asyncLock.resolveQueue.dequeue()! + await new Promise(resolve => { + queuedResolve() + resolve() + }) } - public async release(): Promise { - if (this.resolveQueue.length === 0 && this.acquired) { - this.acquired = false; - return; + private static getAsyncLock (type: AsyncLockType): AsyncLock { + if (!AsyncLock.asyncLocks.has(type)) { + AsyncLock.asyncLocks.set(type, new AsyncLock()) } - const queuedResolve = this.resolveQueue.shift(); - return new Promise((resolve) => { - queuedResolve(); - resolve(); - }); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return AsyncLock.asyncLocks.get(type)! } }