all files / bull/lib/process/ child-pool.js

93.22% Statements 55/59
78.57% Branches 11/14
93.75% Functions 15/16
93.22% Lines 55/59
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                24× 24× 23×   23×         20×       20×     20×   20×   20×   20×           40×   40×   40× 40×         20× 20×       19×         66×         20× 20×       24× 24× 24×     24× 23×                
'use strict';
 
var fork = require('child_process').fork;
var fs = require('fs');
var path = require('path');
var Promise = require('bluebird');
var _ = require('lodash');
 
var ChildPool = function ChildPool() {
  Iif (!(this instanceof ChildPool)) {
    return new ChildPool();
  }
 
  this.retained = {};
  this.free = {};
};
 
ChildPool.prototype.retain = function(processFile) {
  var _this = this;
  return checkProcessorFile(processFile).then(function() {
    var child = _this.getFree(processFile).pop();
 
    if (child) {
      _this.retained[child.pid] = child;
      return child;
    }
 
    // if node process is running with --inspect, don't include that option
    // when spawning the children
    var execArgv = _.filter(process.execArgv, function(arg) {
      return arg.indexOf('--inspect') === -1;
    });
 
    child = fork(path.join(__dirname, './master.js'), {
      execArgv: execArgv
    });
    child.processFile = processFile;
 
    _this.retained[child.pid] = child;
 
    child.on('exit', _this.remove.bind(_this, child));
 
    return initChild(child, processFile).return(child);
  });
};
 
ChildPool.prototype.release = function(child) {
  delete this.retained[child.pid];
  this.getFree(child.processFile).push(child);
};
 
ChildPool.prototype.remove = function(child) {
  delete this.retained[child.pid];
 
  var free = this.getFree(child.processFile);
 
  var childIndex = free.indexOf(child);
  Iif (childIndex > -1) {
    free.splice(childIndex, 1);
  }
};
 
ChildPool.prototype.kill = function(child, signal) {
  child.kill(signal || 'SIGKILL');
  this.remove(child);
};
 
ChildPool.prototype.clean = function() {
  var children = _.values(this.retained).concat(this.getAllFree());
  var _this = this;
  children.forEach(function(child) {
    // TODO: We may want to use SIGKILL if the process does not die after some time.
    _this.kill(child, 'SIGTERM');
  });
 
  this.retained = {};
  this.free = {};
};
 
ChildPool.prototype.getFree = function(id) {
  return (this.free[id] = this.free[id] || []);
};
 
ChildPool.prototype.getAllFree = function() {
  return _.flatten(_.values(this.free));
};
 
var initChild = function(child, processFile) {
  return new Promise(function(resolve) {
    child.send({ cmd: 'init', value: processFile }, resolve);
  });
};
 
var checkProcessorFile = function(processorFile) {
  return new Promise(function(resolve, reject) {
    fs.exists(processorFile, function(stats, err) {
      Iif (err) {
        reject(err);
      } else {
        if (stats) {
          resolve();
        } else {
          reject(new Error('File does not exists'));
        }
      }
    });
  });
};
 
module.exports = ChildPool;