// Partial Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+import { Queue } from './Queue';
+
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() {
this.acquired = false;
- this.resolveQueue = [];
+ this.resolveQueue = new Queue<ResolveType>();
}
public static async acquire(type: AsyncLockType): Promise<void> {
return;
}
return new Promise((resolve) => {
- asyncLock.resolveQueue.push(resolve);
+ asyncLock.resolveQueue.enqueue(resolve);
});
}
public 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();
+ const queuedResolve = asyncLock.resolveQueue.dequeue();
return new Promise((resolve) => {
queuedResolve();
resolve();
--- /dev/null
+// Copyright Jerome Benoit. 2021-2023. All Rights Reserved.
+
+/**
+ * Queue
+ *
+ * @typeParam T - Type of queue items.
+ */
+export class Queue<T> {
+ private items: Record<number, T>;
+ private head: number;
+ private tail: number;
+
+ public constructor() {
+ this.items = {};
+ this.head = 0;
+ this.tail = 0;
+ }
+
+ /**
+ * Get the size of the queue.
+ *
+ * @returns The size of the queue.
+ * @readonly
+ */
+ public get size(): number {
+ return this.tail - this.head;
+ }
+
+ /**
+ * Enqueue an item.
+ *
+ * @param item - Item to enqueue.
+ * @returns The new size of the queue.
+ */
+ public enqueue(item: T): number {
+ this.items[this.tail] = item;
+ this.tail++;
+ return this.size;
+ }
+
+ /**
+ * Dequeue an item.
+ *
+ * @returns The dequeued item or `undefined` if the queue is empty.
+ */
+ public dequeue(): T | undefined {
+ if (this.size <= 0) {
+ return undefined;
+ }
+ const item = this.items[this.head];
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
+ delete this.items[this.head];
+ this.head++;
+ if (this.head === this.tail) {
+ this.head = 0;
+ this.tail = 0;
+ }
+ return item;
+ }
+
+ /**
+ * Peek at the first item.
+ */
+ public peek(): T | undefined {
+ if (this.size <= 0) {
+ return undefined;
+ }
+ return this.items[this.head];
+ }
+}
--- /dev/null
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+import { expect } from 'expect';
+
+import { Queue } from '../../src/utils/Queue';
+
+describe('Queue test suite', () => {
+ it('Verify enqueue() behavior', () => {
+ const queue = new Queue();
+ let rtSize = queue.enqueue(1);
+ expect(queue.size).toBe(1);
+ expect(rtSize).toBe(queue.size);
+ expect((queue as any).head).toBe(0);
+ expect((queue as any).tail).toBe(1);
+ expect((queue as any).items).toStrictEqual({ 0: 1 });
+ rtSize = queue.enqueue(2);
+ expect(queue.size).toBe(2);
+ expect(rtSize).toBe(queue.size);
+ expect((queue as any).head).toBe(0);
+ expect((queue as any).tail).toBe(2);
+ expect((queue as any).items).toStrictEqual({ 0: 1, 1: 2 });
+ rtSize = queue.enqueue(3);
+ expect(queue.size).toBe(3);
+ expect(rtSize).toBe(queue.size);
+ expect((queue as any).head).toBe(0);
+ expect((queue as any).tail).toBe(3);
+ expect((queue as any).items).toStrictEqual({ 0: 1, 1: 2, 2: 3 });
+ });
+
+ it('Verify dequeue() behavior', () => {
+ const queue = new Queue();
+ queue.enqueue(1);
+ queue.enqueue(2);
+ queue.enqueue(3);
+ let rtItem = queue.dequeue();
+ expect(queue.size).toBe(2);
+ expect(rtItem).toBe(1);
+ expect((queue as any).head).toBe(1);
+ expect((queue as any).tail).toBe(3);
+ expect((queue as any).items).toStrictEqual({ 1: 2, 2: 3 });
+ rtItem = queue.dequeue();
+ expect(queue.size).toBe(1);
+ expect(rtItem).toBe(2);
+ expect((queue as any).head).toBe(2);
+ expect((queue as any).tail).toBe(3);
+ expect((queue as any).items).toStrictEqual({ 2: 3 });
+ rtItem = queue.dequeue();
+ expect(queue.size).toBe(0);
+ expect(rtItem).toBe(3);
+ expect((queue as any).head).toBe(0);
+ expect((queue as any).tail).toBe(0);
+ expect((queue as any).items).toStrictEqual({});
+ });
+});