chore(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / utils / AsyncLock.ts
... / ...
CommitLineData
1// Partial Copyright Jerome Benoit. 2021-2024. All Rights Reserved.
2
3import { Queue } from 'mnemonist'
4
5import { isAsyncFunction } from './Utils.js'
6
7export enum AsyncLockType {
8 configuration = 'configuration',
9 performance = 'performance',
10}
11
12type ResolveType = (value: PromiseLike<void> | void) => void
13
14export 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 private static async acquire (type: AsyncLockType): Promise<void> {
25 const asyncLock = AsyncLock.getAsyncLock(type)
26 if (!asyncLock.acquired) {
27 asyncLock.acquired = true
28 return
29 }
30 await new Promise<void>(resolve => {
31 asyncLock.resolveQueue.enqueue(resolve)
32 })
33 }
34
35 private static getAsyncLock (type: AsyncLockType): AsyncLock {
36 if (!AsyncLock.asyncLocks.has(type)) {
37 AsyncLock.asyncLocks.set(type, new AsyncLock())
38 }
39 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
40 return AsyncLock.asyncLocks.get(type)!
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 await new Promise<void>(resolve => {
50 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
51 asyncLock.resolveQueue.dequeue()!()
52 resolve()
53 })
54 }
55
56 public static async runExclusive<T>(type: AsyncLockType, fn: () => Promise<T> | T): Promise<T> {
57 try {
58 await AsyncLock.acquire(type)
59 if (isAsyncFunction(fn)) {
60 return await fn()
61 } else {
62 return fn() as T
63 }
64 } finally {
65 await AsyncLock.release(type)
66 }
67 }
68}