refactor: more coding style fixes
[e-mobility-charging-stations-simulator.git] / src / utils / AsyncLock.ts
1 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
2
3 import { Queue } from 'mnemonist'
4
5 import { Constants } from './Constants.js'
6
7 export enum AsyncLockType {
8 configuration = 'configuration',
9 performance = 'performance',
10 }
11
12 type ResolveType = (value: void | PromiseLike<void>) => void
13
14 export class AsyncLock {
15 private static readonly asyncLocks = new Map<AsyncLockType, AsyncLock>()
16 private acquired: boolean
17 private readonly resolveQueue: Queue<ResolveType>
18
19 private constructor () {
20 this.acquired = false
21 this.resolveQueue = new Queue<ResolveType>()
22 }
23
24 public static async runExclusive<T>(type: AsyncLockType, fn: () => T | Promise<T>): Promise<T> {
25 return await AsyncLock.acquire(type)
26 .then(fn)
27 .finally(() => {
28 AsyncLock.release(type).catch(Constants.EMPTY_FUNCTION)
29 })
30 }
31
32 private static async acquire (type: AsyncLockType): Promise<void> {
33 const asyncLock = AsyncLock.getAsyncLock(type)
34 if (!asyncLock.acquired) {
35 asyncLock.acquired = true
36 return
37 }
38 await new Promise<void>((resolve) => {
39 asyncLock.resolveQueue.enqueue(resolve)
40 })
41 }
42
43 private static async release (type: AsyncLockType): Promise<void> {
44 const asyncLock = AsyncLock.getAsyncLock(type)
45 if (asyncLock.resolveQueue.size === 0 && asyncLock.acquired) {
46 asyncLock.acquired = false
47 return
48 }
49 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
50 const queuedResolve = asyncLock.resolveQueue.dequeue()!
51 await new Promise<void>((resolve) => {
52 queuedResolve()
53 resolve()
54 })
55 }
56
57 private static getAsyncLock (type: AsyncLockType): AsyncLock {
58 if (!AsyncLock.asyncLocks.has(type)) {
59 AsyncLock.asyncLocks.set(type, new AsyncLock())
60 }
61 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
62 return AsyncLock.asyncLocks.get(type)!
63 }
64 }