@v4fire/core
Version:
V4Fire core library
170 lines (129 loc) • 3.05 kB
text/typescript
/*!
* V4Fire Core
* https://github.com/V4Fire/Core
*
* Released under the MIT license
* https://github.com/V4Fire/Core/blob/master/LICENSE
*/
/**
* [[include:core/queue/worker/merge/README.md]]
* @packageDocumentation
*/
import WorkerQueue from 'core/queue/worker/interface';
import type {
Task,
Tasks,
HashFn,
QueueWorker,
WorkerQueueOptions
} from 'core/queue/worker/merge/interface';
export * from 'core/queue/worker/merge/interface';
/**
* Implementation of a worker queue data structure with support of task merging by a specified hash function
*
* @typeparam T - the task element
* @typeparam V - the worker value
*/
export default class MergeWorkerQueue<T, V = unknown> extends WorkerQueue<T, V> {
override readonly Tasks!: Tasks<string>;
override get head(): CanUndef<T> {
if (this.length === 0) {
return undefined;
}
const obj = this.tasksMap.get(this.tasks.head!);
return obj?.task;
}
/**
* A map of registered tasks
*/
protected tasksMap: Map<string, Task<T, V>> = new Map();
/**
* A function to calculate task hashes
*/
protected readonly hashFn: HashFn<T>;
/**
* @override
* @param worker
* @param [opts] - additional options
*/
constructor(worker: QueueWorker<T, V>, opts: WorkerQueueOptions<T>) {
super(worker, opts);
this.hashFn = opts.hashFn ?? Object.fastHash.bind(Object);
}
override push(task: T): Promise<V> {
const
hash = this.hashFn(task);
let
taskObj = this.tasksMap.get(hash);
if (taskObj == null) {
let
resolve;
const promise = new Promise<V>((r) => {
resolve = r;
});
taskObj = {task, promise, resolve};
this.tasksMap.set(hash, taskObj);
this.tasks.push(hash);
}
this.start();
return taskObj.promise;
}
override pop(): CanUndef<T> {
if (this.length === 0) {
return;
}
const
{head} = this;
this.tasksMap.delete(this.tasks.head!);
this.tasks.shift();
return head;
}
override clear(): void {
if (this.length > 0) {
super.clear();
this.tasksMap = new Map<string, Task<T, V>>();
}
}
override clone(): MergeWorkerQueue<T, V> {
const
newQueue = new MergeWorkerQueue<T, V>(this.worker, {});
Object.assign(newQueue, this);
newQueue.tasksMap = new Map(this.tasksMap);
if (this.tasks.clone != null) {
newQueue.tasks = this.tasks.clone();
} else {
const
tasks = this.createTasks();
for (const task of this.tasks) {
tasks.push(task);
}
newQueue.tasks = tasks;
}
return newQueue;
}
protected override perform(): void {
if (this.length === 0) {
this.activeWorkers--;
return;
}
const
hash = this.tasks.shift();
if (hash === undefined) {
return;
}
const
taskObj = this.tasksMap.get(hash);
if (taskObj == null) {
return;
}
const
// eslint-disable-next-line @typescript-eslint/unbound-method
{task, promise, resolve} = taskObj;
const cb = () => {
this.tasksMap.delete(hash);
return this.deferPerform();
};
promise.then(cb, cb);
this.resolveTask(task, resolve);
}
}