import { BasicPool } from './BasicPool.js'; import type { IWorkerPool, PooledWorker } from './IWorkerPool.js'; const poolPerFile = new Map>(); const pendingTerminationPerFile = new Map>(); const activeUsersPerFile = new Map(); function cancelPendingTerminationIfAny(workerFileUrl: URL) { const key = workerFileUrl.toString(); const pendingTermination = pendingTerminationPerFile.get(key); if (pendingTermination === undefined) { return; } clearTimeout(pendingTermination); } /** * Cross-properties pool able to re-use the workers for many properties */ export class GlobalPool implements IWorkerPool { private readonly internalPool: BasicPool; /** * Instantiate a new pool of workers * @param workerFileUrl - URL of the script for workers */ constructor(private readonly workerFileUrl: URL) { const key = workerFileUrl.toString(); const existingPool = poolPerFile.get(key); if (existingPool !== undefined) { this.internalPool = existingPool as BasicPool; } else { const freshPool = new BasicPool(workerFileUrl); this.internalPool = freshPool; poolPerFile.set(key, freshPool as BasicPool); } activeUsersPerFile.set(key, (activeUsersPerFile.get(key) ?? 0) + 1); cancelPendingTerminationIfAny(workerFileUrl); } spawnNewWorker(): Promise> { cancelPendingTerminationIfAny(this.workerFileUrl); return this.internalPool.spawnNewWorker(); } getFirstAvailableWorker(): PooledWorker | undefined { cancelPendingTerminationIfAny(this.workerFileUrl); return this.internalPool.getFirstAvailableWorker(); } terminateAllWorkers(): Promise { const key = this.workerFileUrl.toString(); const activeUsers = (activeUsersPerFile.get(key) ?? 0) - 1; if (activeUsers > 0) { activeUsersPerFile.set(key, activeUsers); return Promise.resolve(); } activeUsersPerFile.delete(key); cancelPendingTerminationIfAny(this.workerFileUrl); pendingTerminationPerFile.set( key, setTimeout(() => { // oxlint-disable-next-line typescript/no-floating-promises this.internalPool.terminateAllWorkers(); }, 0), ); return Promise.resolve(); } }