build: switch to NodeNext module resolution
[e-mobility-charging-stations-simulator.git] / src / utils / AsyncLock.ts
CommitLineData
b9b617a2
JB
1// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
2
f31d1d0c 3import Queue from 'mnemonist/queue.js';
4f9327bf 4
a6ef1ece 5import { Constants } from './Constants.js';
0ebf7c2e 6
1227a6f1
JB
7export enum AsyncLockType {
8 configuration = 'configuration',
9 performance = 'performance',
10}
11
4f9327bf
JB
12type ResolveType = (value: void | PromiseLike<void>) => void;
13
1227a6f1 14export class AsyncLock {
dd485b56 15 private static readonly asyncLocks = new Map<AsyncLockType, AsyncLock>();
7e0bf360 16 private acquired: boolean;
4f9327bf 17 private readonly resolveQueue: Queue<ResolveType>;
1227a6f1 18
42486f23 19 private constructor() {
1227a6f1 20 this.acquired = false;
4f9327bf 21 this.resolveQueue = new Queue<ResolveType>();
1227a6f1
JB
22 }
23
0ebf7c2e
JB
24 public static async runExclusive<T>(type: AsyncLockType, fn: () => T | Promise<T>): Promise<T> {
25 return 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> {
dd485b56
JB
33 const asyncLock = AsyncLock.getAsyncLock(type);
34 if (!asyncLock.acquired) {
35 asyncLock.acquired = true;
acf727c7 36 return;
1227a6f1 37 }
474d4ffc 38 return new Promise<void>((resolve) => {
4f9327bf 39 asyncLock.resolveQueue.enqueue(resolve);
acf727c7 40 });
1227a6f1
JB
41 }
42
0ebf7c2e 43 private static async release(type: AsyncLockType): Promise<void> {
dd485b56 44 const asyncLock = AsyncLock.getAsyncLock(type);
4f9327bf 45 if (asyncLock.resolveQueue.size === 0 && asyncLock.acquired) {
dd485b56 46 asyncLock.acquired = false;
1227a6f1
JB
47 return;
48 }
e1d9a0f4 49 const queuedResolve = asyncLock.resolveQueue.dequeue()!;
474d4ffc 50 return new Promise<void>((resolve) => {
1227a6f1
JB
51 queuedResolve();
52 resolve();
53 });
54 }
dd485b56
JB
55
56 private static getAsyncLock(type: AsyncLockType): AsyncLock {
57 if (!AsyncLock.asyncLocks.has(type)) {
42486f23 58 AsyncLock.asyncLocks.set(type, new AsyncLock());
dd485b56 59 }
e1d9a0f4 60 return AsyncLock.asyncLocks.get(type)!;
dd485b56 61 }
1227a6f1 62}