fix(simulator): ensure configuration file reload will restart the
[e-mobility-charging-stations-simulator.git] / src / utils / AsyncLock.ts
index 7f44bd257251f32cde860ce6a40d6eb82cf1a51c..fe984cebdef97d8043d0f29f6fb9ba99ec502f3c 100644 (file)
@@ -1,39 +1,53 @@
 // Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
 
+import Queue from 'mnemonist/queue.js';
+
+import { Constants } from './Constants';
+
 export enum AsyncLockType {
   configuration = 'configuration',
   performance = 'performance',
 }
 
+type ResolveType = (value: void | PromiseLike<void>) => void;
+
 export class AsyncLock {
   private static readonly asyncLocks = new Map<AsyncLockType, AsyncLock>();
   private acquired: boolean;
-  private readonly resolveQueue: ((value: void | PromiseLike<void>) => void)[];
+  private readonly resolveQueue: Queue<ResolveType>;
 
-  private constructor(private readonly type: AsyncLockType) {
+  private constructor() {
     this.acquired = false;
-    this.resolveQueue = [];
+    this.resolveQueue = new Queue<ResolveType>();
   }
 
-  public static async acquire(type: AsyncLockType): Promise<void> {
+  public static async runExclusive<T>(type: AsyncLockType, fn: () => T | Promise<T>): Promise<T> {
+    return AsyncLock.acquire(type)
+      .then(fn)
+      .finally(() => {
+        AsyncLock.release(type).catch(Constants.EMPTY_FUNCTION);
+      });
+  }
+
+  private static async acquire(type: AsyncLockType): Promise<void> {
     const asyncLock = AsyncLock.getAsyncLock(type);
     if (!asyncLock.acquired) {
       asyncLock.acquired = true;
-    } else {
-      return new Promise((resolve) => {
-        asyncLock.resolveQueue.push(resolve);
-      });
+      return;
     }
+    return new Promise<void>((resolve) => {
+      asyncLock.resolveQueue.enqueue(resolve);
+    });
   }
 
-  public static async release(type: AsyncLockType): Promise<void> {
+  private static async release(type: AsyncLockType): Promise<void> {
     const asyncLock = AsyncLock.getAsyncLock(type);
-    if (asyncLock.resolveQueue.length === 0 && asyncLock.acquired) {
+    if (asyncLock.resolveQueue.size === 0 && asyncLock.acquired) {
       asyncLock.acquired = false;
       return;
     }
-    const queuedResolve = asyncLock.resolveQueue.shift();
-    return new Promise((resolve) => {
+    const queuedResolve = asyncLock.resolveQueue.dequeue()!;
+    return new Promise<void>((resolve) => {
       queuedResolve();
       resolve();
     });
@@ -41,8 +55,8 @@ export class AsyncLock {
 
   private static getAsyncLock(type: AsyncLockType): AsyncLock {
     if (!AsyncLock.asyncLocks.has(type)) {
-      AsyncLock.asyncLocks.set(type, new AsyncLock(type));
+      AsyncLock.asyncLocks.set(type, new AsyncLock());
     }
-    return AsyncLock.asyncLocks.get(type);
+    return AsyncLock.asyncLocks.get(type)!;
   }
 }