You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
3.4 KiB
145 lines
3.4 KiB
1 month ago
|
'use strict';
|
||
|
|
||
|
const fork = require('child_process').fork;
|
||
|
const path = require('path');
|
||
|
const _ = require('lodash');
|
||
|
const getPort = require('get-port');
|
||
|
const { killAsync } = require('./utils');
|
||
|
|
||
|
const CHILD_KILL_TIMEOUT = 30000;
|
||
|
|
||
|
const ChildPool = function ChildPool() {
|
||
|
if (!(this instanceof ChildPool)) {
|
||
|
return new ChildPool();
|
||
|
}
|
||
|
|
||
|
this.retained = {};
|
||
|
this.free = {};
|
||
|
};
|
||
|
|
||
|
const convertExecArgv = function(execArgv) {
|
||
|
const standard = [];
|
||
|
const promises = [];
|
||
|
|
||
|
_.forEach(execArgv, arg => {
|
||
|
if (arg.indexOf('--inspect') === -1) {
|
||
|
standard.push(arg);
|
||
|
} else {
|
||
|
const argName = arg.split('=')[0];
|
||
|
promises.push(
|
||
|
getPort().then(port => {
|
||
|
return `${argName}=${port}`;
|
||
|
})
|
||
|
);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return Promise.all(promises).then(convertedArgs => {
|
||
|
return standard.concat(convertedArgs);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
ChildPool.prototype.retain = function(processFile) {
|
||
|
const _this = this;
|
||
|
let child = _this.getFree(processFile).pop();
|
||
|
|
||
|
if (child) {
|
||
|
_this.retained[child.pid] = child;
|
||
|
return Promise.resolve(child);
|
||
|
}
|
||
|
|
||
|
return convertExecArgv(process.execArgv).then(execArgv => {
|
||
|
child = fork(path.join(__dirname, './master.js'), {
|
||
|
execArgv
|
||
|
});
|
||
|
child.processFile = processFile;
|
||
|
|
||
|
_this.retained[child.pid] = child;
|
||
|
|
||
|
child.on('exit', _this.remove.bind(_this, child));
|
||
|
|
||
|
return initChild(child, child.processFile)
|
||
|
.then(() => {
|
||
|
return child;
|
||
|
})
|
||
|
.catch(err => {
|
||
|
this.remove(child);
|
||
|
throw err;
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
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];
|
||
|
|
||
|
const free = this.getFree(child.processFile);
|
||
|
|
||
|
const childIndex = free.indexOf(child);
|
||
|
if (childIndex > -1) {
|
||
|
free.splice(childIndex, 1);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ChildPool.prototype.kill = function(child, signal) {
|
||
|
this.remove(child);
|
||
|
return killAsync(child, signal || 'SIGKILL', CHILD_KILL_TIMEOUT);
|
||
|
};
|
||
|
|
||
|
ChildPool.prototype.clean = function() {
|
||
|
const children = _.values(this.retained).concat(this.getAllFree());
|
||
|
this.retained = {};
|
||
|
this.free = {};
|
||
|
|
||
|
const allKillPromises = [];
|
||
|
children.forEach(child => {
|
||
|
allKillPromises.push(this.kill(child, 'SIGTERM'));
|
||
|
});
|
||
|
return Promise.all(allKillPromises).then(() => {});
|
||
|
};
|
||
|
|
||
|
ChildPool.prototype.getFree = function(id) {
|
||
|
return (this.free[id] = this.free[id] || []);
|
||
|
};
|
||
|
|
||
|
ChildPool.prototype.getAllFree = function() {
|
||
|
return _.flatten(_.values(this.free));
|
||
|
};
|
||
|
|
||
|
async function initChild(child, processFile) {
|
||
|
const onComplete = new Promise((resolve, reject) => {
|
||
|
const onMessageHandler = msg => {
|
||
|
if (msg.cmd === 'init-complete') {
|
||
|
resolve();
|
||
|
} else if (msg.cmd === 'error') {
|
||
|
reject(msg.error);
|
||
|
}
|
||
|
child.off('message', onMessageHandler);
|
||
|
};
|
||
|
child.on('message', onMessageHandler);
|
||
|
});
|
||
|
|
||
|
await new Promise(resolve =>
|
||
|
child.send({ cmd: 'init', value: processFile }, resolve)
|
||
|
);
|
||
|
await onComplete;
|
||
|
}
|
||
|
function ChildPoolSingleton(isSharedChildPool = false) {
|
||
|
if (isSharedChildPool === false) {
|
||
|
return new ChildPool();
|
||
|
} else if (
|
||
|
!(this instanceof ChildPool) &&
|
||
|
ChildPoolSingleton.instance === undefined
|
||
|
) {
|
||
|
ChildPoolSingleton.instance = new ChildPool();
|
||
|
}
|
||
|
|
||
|
return ChildPoolSingleton.instance;
|
||
|
}
|
||
|
|
||
|
module.exports = ChildPoolSingleton;
|