UNPKG

hexo

Version:

A fast, simple & powerful blog framework, powered by Node.js.

207 lines (170 loc) 3.67 kB
var child_process = require('child_process'), cpus = require('os').cpus().length; if (typeof setImmediate !== 'undefined'){ var nextTick = function(fn){ setImmediate(fn); }; } else { var nextTick = function(fn){ process.nextTick(fn); }; } /** * Thread pool. * * @class Pool * @param {String} worker * @param {Number} [concurrency] Default to the number of CPUs. * @namespace util * @constructor * @module hexo */ var Pool = module.exports = function Pool(worker, concurrency){ /** * Tasks. * * @property tasks * @type Array * @private */ this.tasks = []; /** * Workers. * * @property workers * @type Array * @private */ this.workers = []; /** * Concurrency. * * @property concurrency * @type Number */ this.concurrency = concurrency || cpus; /** * This function is invoked when the last task in the pool is done. * * @property drain * @type Function */ this.drain = function(){}; /** * This function is invoked when the number of pending tasks equals to the concurrency of the pool. * * @property saturated * @type Function */ this.saturated = function(){}; /** * This function is invoked when the last task in the pool is started. * * @property empty * @type Function */ this.empty = function(){}; for (var i = 0; i < this.concurrency; i++){ this.workers.push(child_process.fork(worker)); } }; /** * Inserts a task to the pool. * * @method _insert * @param {Array|Object} tasks * @param {Boolean} first * @param {Function} [callback] * @private */ Pool.prototype._insert = function(tasks, first, callback){ if (!Array.isArray(tasks)) tasks = [tasks]; var self = this; tasks.forEach(function(task){ var item = { data: task, callback: typeof callback === 'function' ? callback : function(){} }; if (first){ self.tasks.unshift(item); } else { self.tasks.push(item); } if (self.tasks.length === self.concurrency){ self.saturated(); } nextTick(function(){ self.process(); }); }); }; /** * Starts distributing tasks to each worker. * * @method process * @private */ Pool.prototype.process = function(){ if (this.tasks.length && this.workers.length){ var task = this.tasks.shift(), worker = this.workers.shift(), self = this; if (!this.tasks.length) this.empty(); var removeAllListeners = function(){ worker.removeAllListeners('error') .removeAllListeners('message'); }; worker .on('error', function(err){ task.callback(err); removeAllListeners(); }) .on('message', function(){ task.callback.apply(task, arguments); self.workers.push(worker); if (!self.tasks.length && self.workers.length == self.concurrency) self.drain(); removeAllListeners(); self.process(); }) .send(task.data); } }; /** * Adds a task to last of queue. * * @method push * @param {Array|Object} tasks * @param {Function} [callback] */ Pool.prototype.push = function(tasks, callback){ this._insert(tasks, false, callback); }; /** * Adds a task to first of queue. * * @method unshift * @param {Array|Object} tasks * @param {Function} [callback] */ Pool.prototype.unshift = function(tasks, callback){ this._insert(tasks, true, callback); }; /** * Terminates all workers. * * @method end */ Pool.prototype.end = function(){ this.workers.forEach(function(worker){ worker.kill(); }); }; /** * Returns the number of pending tasks. * * @method length * @return {Number} */ Pool.prototype.length = function(){ return this.tasks.length; };