UNPKG

parallel-universe

Version:

The set of async flow control structures and promise utils.

104 lines (100 loc) 3.61 kB
'use strict'; var AbortablePromise = require('./AbortablePromise.js'); var AsyncQueue = require('./AsyncQueue.js'); var utils = require('./utils.js'); var Worker = require('./Worker.js'); /** * The callback execution pool that can execute limited number of callbacks in parallel while other submitted callbacks * wait in the queue. */ class WorkPool { /** * Creates a new {@link WorkPool} instance that uses given number of workers. * * @param size The number of workers in the pool. */ constructor(size = 1) { /** * The queue that holds submitted jobs. */ this._jobQueue = new AsyncQueue.AsyncQueue(); /** * Active workers and workers with pending termination. */ this._workers = []; this.setSize(size); } /** * The number of active workers in the pool. */ get size() { let size = 0; for (const worker of this._workers) { if (!worker.isTerminated) { ++size; } } return size; } /** * Changes the size of the pool by spawning or terminating workers. If the size of the pool is reduced, then * corresponding workers are terminated and if they were processing jobs, those jobs are instantly aborted. * * @param size The new size of the pool. * @param reason The reason that is used to reject pending job promises that are processed by terminated workers. Only * applicable if the pool is downsized. * @return The promise that is fulfilled when the number of workers matches the requested size: excessive workers were * deleted or additional workers were spawned. */ setSize(size, reason) { const { _workers } = this; const promises = []; // Terminate excessive workers for (let i = 0; i < _workers.length; ++i) { const worker = _workers[i]; if (worker.isTerminated) { // Worker is terminated but job abortion is pending promises.push(worker.terminate(reason)); continue; } if (--size >= 0) { continue; } // Remove worker from the pool after its termination is completed promises.push(worker.terminate(reason).then(() => { _workers.splice(_workers.indexOf(worker), 1); })); } // Spawn additional workers for (let i = 0; i < size; ++i) { _workers.push(new Worker.Worker(this._jobQueue)); } return Promise.all(promises).then(utils.noop); } /** * Submits a new callback that should be executed by the worker in the pool. * * @param cb The callback to invoke. * @template T The callback result. * @returns The promise that is fulfilled with the callback result. */ submit(cb) { return new AbortablePromise.AbortablePromise((resolve, reject, signal) => { this._jobQueue.append({ cb, resolve, reject, signal }); }); } /** * Aborts all pending jobs and returns promise that is fulfilled when all workers are terminated. * * This operation preserves the size of the pool intact. * * @param reason The reason that is used to reject all pending job promises. * @return The promise that is fulfilled when all workers are terminated. */ abort(reason) { const size = this.size; this.setSize(0, reason); return this.setSize(size); } } exports.WorkPool = WorkPool;