UNPKG

manticore

Version:

Mythical multi-process worker pool

189 lines (188 loc) 6.44 kB
'use strict'; var __extends = this.__extends || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); }; var events = require('events'); var child_process = require('child_process'); var typeOf = require('type-detect'); var through2 = require('through2'); var JSONStream = require('JSONStream'); var lib = require('./lib'); var jobI = 0; var Job = (function () { function Job(task, params, callback) { this.attempts = 1; this.send = false; this.id = 'job.' + jobI++; this.task = task; this.params = params; this.callback = callback; } Job.prototype.toString = function () { return this.id + '-' + this.task; }; return Job; })(); exports.Job = Job; var Worker = (function (_super) { __extends(Worker, _super); function Worker(options) { var _this = this; _super.call(this); this.ready = false; this.jobs = Object.create(null); this._activeCount = 0; this.options = options; this.idleTimer = new lib.BumpTimeout(this.options.idleTimeout, function () { if (_this.activeCount === 0) { _this.status('idle timeout'); _this.kill(); } else { _this.idleTimer.next(); } }); var args = [ this.options.worker ]; var opts = { cwd: process.cwd(), stdio: ['ignore', process.stdout, process.stderr, 'pipe', 'pipe', 'ipc'] }; this.child = child_process.spawn(process.execPath, args, opts); this.id = 'worker.' + this.child.pid; this.write = JSONStream.stringify(false); this.write.pipe(through2()).pipe(this.child.stdio[lib.WORK_TO_CLIENT]); this.read = this.child.stdio[lib.CLIENT_TO_WORK].pipe(JSONStream.parse(true)); this.read.on('data', function (msg) { if (msg.type === lib.TASK_RESULT) { if (msg.id in _this.jobs) { var job = _this.jobs[msg.id]; _this._activeCount--; delete _this.jobs[msg.id]; // upfix Error if (msg.error) { msg.error.toString = function () { return msg.message; }; } _this.status('completed', job, Math.round(msg.duration) + 'ms'); job.callback(msg.error, msg.result); _this.emit(lib.TASK_RESULT, job); _this.idleTimer.next(); } } }); this.child.stdio[lib.WORK_TO_CLIENT].on('close', function () { _this.status('client closed WORK_TO_CLIENT stream'); _this.kill(); }); this.child.stdio[lib.CLIENT_TO_WORK].on('close', function () { _this.status('client closed CLIENT_TO_WORK stream'); _this.kill(); }); this.child.send({ a: 1 }, null); var onMessage = function (msg) { if (msg.type === lib.WORKER_READY) { _this.status('ready'); _this.ready = true; _this.flushWaiting(); } else { _this.status('unknown message', typeOf(msg), JSON.stringify(msg, null, 3)); } }; var onError = function (error) { _this.status('job error', error); _this.kill(); }; var onClose = function (code) { _this.status('job close', code); _this.kill(); }; this.child.on('message', onMessage); this.child.on('error', onError); this.child.on('close', onClose); this.kill = function () { _this.status('kill'); _this.write.removeAllListeners(); _this.read.removeAllListeners(); if (_this.child) { _this.child.stdio[lib.WORK_TO_CLIENT].removeAllListeners(); _this.child.stdio[lib.CLIENT_TO_WORK].removeAllListeners(); _this.child.removeAllListeners(); _this.child.kill('SIGKILL'); _this.child = null; } _this.ready = false; _this.emit(lib.WORKER_DOWN); for (var id in _this.jobs) { var job = _this.jobs[id]; _this._activeCount--; delete _this.jobs[id]; _this.emit(lib.TASK_ABORT, job); } _this.idleTimer.clear(); _this.removeAllListeners(); }; } Worker.prototype.run = function (job) { if (!this.child) { this.emit(lib.TASK_ABORT, job); this.kill(); return; } this.jobs[job.id] = job; this._activeCount++; this.idleTimer.next(); if (this.ready) { this.send(job); } }; Worker.prototype.send = function (job) { if (job.send) { return; } this.status('started', job); var msg = { type: lib.TASK_RUN, task: job.task, id: job.id, params: job.params }; job.send = true; // this.child.send(msg, null); this.write.write(msg); }; Worker.prototype.flushWaiting = function () { this.idleTimer.next(); for (var id in this.jobs) { var job = this.jobs[id]; if (!job.send) { this.send(job); } } }; Worker.prototype.status = function () { var message = []; for (var _i = 0; _i < (arguments.length - 0); _i++) { message[_i] = arguments[_i + 0]; } if (this.options.emit) { this.emit(lib.STATUS, this + '; ' + message.join('; ')); } }; Object.defineProperty(Worker.prototype, "activeCount", { get: function () { return this._activeCount; }, enumerable: true, configurable: true }); Worker.prototype.toString = function () { return this.id + ' <' + (this.child ? (this.activeCount + '/' + this.options.paralel) : 'killed') + '>'; }; return Worker; })(events.EventEmitter); exports.Worker = Worker;