Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #689 from OptimalBits/threaded-process
implementation threaded processors
- Loading branch information
Showing
12 changed files
with
338 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
|
||
var fork = require('child_process').fork; | ||
var path = require('path'); | ||
var pool = {}; | ||
var Promise = require('bluebird'); | ||
|
||
module.exports.retain = function(processFile){ | ||
return new Promise(function(resolve, rejected) { | ||
var keys = Object.keys(pool); | ||
for(var i=0; i<keys.length; i++){ | ||
var child = pool[keys[i]]; | ||
if(!child.retained){ | ||
child.retained = true; | ||
return resolve(child.subprocess); | ||
} | ||
} | ||
|
||
try{ | ||
var child = fork(path.join(__dirname, './master.js')); | ||
|
||
child.on('exit', function(code, signal){ | ||
console.error('Child process exited', child.pid, code, signal); | ||
delete pool[child.pid]; | ||
}); | ||
|
||
pool[child.pid] = { | ||
subprocess: child, | ||
retained: true | ||
}; | ||
|
||
child.send({ | ||
cmd: 'init', | ||
value: processFile | ||
}, function() { | ||
resolve(child); | ||
}); | ||
}catch(err){ | ||
reject(err); | ||
} | ||
}); | ||
}; | ||
|
||
module.exports.release = function(child){; | ||
pool[child.pid].retained = false; | ||
}; | ||
|
||
module.exports.clean = function(){ | ||
var keys = Object.keys(pool); | ||
for(var i=0; i<keys.length; i++){ | ||
pool[keys[i]].subprocess.kill(); | ||
delete pool[keys[i]]; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/** | ||
* Master of child processes. Handles communication between the | ||
* processor and the main process. | ||
* | ||
*/ | ||
var status; | ||
var processor; | ||
var Promise = require('bluebird'); | ||
|
||
// https://stackoverflow.com/questions/18391212/is-it-not-possible-to-stringify-an-error-using-json-stringify | ||
if (!('toJSON' in Error.prototype)){ | ||
Object.defineProperty(Error.prototype, 'toJSON', { | ||
value: function () { | ||
var alt = {}; | ||
|
||
Object.getOwnPropertyNames(this).forEach(function (key) { | ||
alt[key] = this[key]; | ||
}, this); | ||
|
||
return alt; | ||
}, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} | ||
|
||
process.on('message', function(msg) { | ||
|
||
switch(msg.cmd){ | ||
case 'init': | ||
processor = require(msg.value); | ||
status = 'IDLE'; | ||
break; | ||
|
||
case 'start': | ||
if(status !== 'IDLE'){ | ||
return process.send({ | ||
cmd: 'error', | ||
err: new Error('cannot start a not idling child process') | ||
}); | ||
} | ||
status = 'STARTED'; | ||
Promise.resolve(processor(wrapJob(msg.job)) || {}).then( function(result) { | ||
process.send({ | ||
cmd: 'completed', | ||
value: result | ||
}); | ||
}, function(err) { | ||
process.send({ | ||
cmd: 'failed', | ||
value: err | ||
}); | ||
}).finally(function(){ | ||
status = 'IDLE'; | ||
}); | ||
break; | ||
case 'stop': | ||
break; | ||
} | ||
}); | ||
|
||
function wrapJob(job){ | ||
job.progress = function(progress){ | ||
process.send({ | ||
cmd: 'progress', | ||
value: progress | ||
}); | ||
}; | ||
return job; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
var Promise = require('bluebird'); | ||
var childPool = require('./child-pool'); | ||
|
||
module.exports = function(processFile){ | ||
return function process(job){ | ||
return childPool.retain(processFile).then(function(child){ | ||
|
||
child.send({ | ||
cmd: 'start', | ||
job: job | ||
}); | ||
|
||
var done = new Promise(function(resolve, reject) { | ||
function handler(msg){ | ||
switch(msg.cmd){ | ||
case 'completed': | ||
child.removeListener('message', handler); | ||
resolve(msg.value); | ||
break; | ||
case 'failed': | ||
case 'error': | ||
child.removeListener('message', handler); | ||
reject(msg.value); | ||
break; | ||
case 'progress': | ||
job.progress(msg.value); | ||
break; | ||
} | ||
} | ||
|
||
child.on('message', handler); | ||
}); | ||
|
||
return done.finally( function(){ | ||
childPool.release(child); | ||
}); | ||
}); | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* A processor file to be used in tests. | ||
* | ||
*/ | ||
|
||
var Promise = require('bluebird'); | ||
|
||
module.exports = function(job){ | ||
return Promise.delay(500).then(function(){ | ||
return 42; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* A processor file to be used in tests. | ||
* | ||
*/ | ||
|
||
var Promise = require('bluebird'); | ||
|
||
module.exports = function(job){ | ||
return Promise.delay(500).then(function(){ | ||
throw new Error('Manually failed processor'); | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
/** | ||
* A processor file to be used in tests. | ||
* | ||
*/ | ||
|
||
var Promise = require('bluebird'); | ||
|
||
module.exports = function(job){ | ||
return Promise.delay(50).then(function(){ | ||
job.progress(10); | ||
return Promise.delay(100); | ||
}).then(function(){ | ||
job.progress(27); | ||
return Promise.delay(150); | ||
}).then(function(){ | ||
job.progress(78); | ||
return Promise.delay(100); | ||
}).then(function(){ | ||
return job.progress(100); | ||
}).then(function(){ | ||
return 37; | ||
}); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.