167 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			167 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | var _types = require('../types'); | ||
|  | 
 | ||
|  | function _objectSpread(target) { | ||
|  |   for (var i = 1; i < arguments.length; i++) { | ||
|  |     var source = arguments[i] != null ? arguments[i] : {}; | ||
|  |     var ownKeys = Object.keys(source); | ||
|  |     if (typeof Object.getOwnPropertySymbols === 'function') { | ||
|  |       ownKeys = ownKeys.concat( | ||
|  |         Object.getOwnPropertySymbols(source).filter(function(sym) { | ||
|  |           return Object.getOwnPropertyDescriptor(source, sym).enumerable; | ||
|  |         }) | ||
|  |       ); | ||
|  |     } | ||
|  |     ownKeys.forEach(function(key) { | ||
|  |       _defineProperty(target, key, source[key]); | ||
|  |     }); | ||
|  |   } | ||
|  |   return target; | ||
|  | } | ||
|  | 
 | ||
|  | function _defineProperty(obj, key, value) { | ||
|  |   if (key in obj) { | ||
|  |     Object.defineProperty(obj, key, { | ||
|  |       value: value, | ||
|  |       enumerable: true, | ||
|  |       configurable: true, | ||
|  |       writable: true | ||
|  |     }); | ||
|  |   } else { | ||
|  |     obj[key] = value; | ||
|  |   } | ||
|  |   return obj; | ||
|  | } | ||
|  | 
 | ||
|  | let file = null; | ||
|  | let setupArgs = []; | ||
|  | let initialized = false; | ||
|  | /** | ||
|  |  * This file is a small bootstrapper for workers. It sets up the communication | ||
|  |  * between the worker and the parent process, interpreting parent messages and | ||
|  |  * sending results back. | ||
|  |  * | ||
|  |  * The file loaded will be lazily initialized the first time any of the workers | ||
|  |  * is called. This is done for optimal performance: if the farm is initialized, | ||
|  |  * but no call is made to it, child Node processes will be consuming the least | ||
|  |  * possible amount of memory. | ||
|  |  * | ||
|  |  * If an invalid message is detected, the child will exit (by throwing) with a | ||
|  |  * non-zero exit code. | ||
|  |  */ | ||
|  | 
 | ||
|  | process.on('message', request => { | ||
|  |   switch (request[0]) { | ||
|  |     case _types.CHILD_MESSAGE_INITIALIZE: | ||
|  |       const init = request; | ||
|  |       file = init[2]; | ||
|  |       setupArgs = request[3]; | ||
|  |       break; | ||
|  | 
 | ||
|  |     case _types.CHILD_MESSAGE_CALL: | ||
|  |       const call = request; | ||
|  |       execMethod(call[2], call[3]); | ||
|  |       break; | ||
|  | 
 | ||
|  |     case _types.CHILD_MESSAGE_END: | ||
|  |       end(); | ||
|  |       break; | ||
|  | 
 | ||
|  |     default: | ||
|  |       throw new TypeError( | ||
|  |         'Unexpected request from parent process: ' + request[0] | ||
|  |       ); | ||
|  |   } | ||
|  | }); | ||
|  | 
 | ||
|  | function reportSuccess(result) { | ||
|  |   if (!process || !process.send) { | ||
|  |     throw new Error('Child can only be used on a forked process'); | ||
|  |   } | ||
|  | 
 | ||
|  |   process.send([_types.PARENT_MESSAGE_OK, result]); | ||
|  | } | ||
|  | 
 | ||
|  | function reportClientError(error) { | ||
|  |   return reportError(error, _types.PARENT_MESSAGE_CLIENT_ERROR); | ||
|  | } | ||
|  | 
 | ||
|  | function reportInitializeError(error) { | ||
|  |   return reportError(error, _types.PARENT_MESSAGE_SETUP_ERROR); | ||
|  | } | ||
|  | 
 | ||
|  | function reportError(error, type) { | ||
|  |   if (!process || !process.send) { | ||
|  |     throw new Error('Child can only be used on a forked process'); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (error == null) { | ||
|  |     error = new Error('"null" or "undefined" thrown'); | ||
|  |   } | ||
|  | 
 | ||
|  |   process.send([ | ||
|  |     type, | ||
|  |     error.constructor && error.constructor.name, | ||
|  |     error.message, | ||
|  |     error.stack, | ||
|  |     typeof error === 'object' ? _objectSpread({}, error) : error | ||
|  |   ]); | ||
|  | } | ||
|  | 
 | ||
|  | function end() { | ||
|  |   const main = require(file); | ||
|  | 
 | ||
|  |   if (!main.teardown) { | ||
|  |     exitProcess(); | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   execFunction(main.teardown, main, [], exitProcess, exitProcess); | ||
|  | } | ||
|  | 
 | ||
|  | function exitProcess() { | ||
|  |   process.exit(0); | ||
|  | } | ||
|  | 
 | ||
|  | function execMethod(method, args) { | ||
|  |   const main = require(file); | ||
|  | 
 | ||
|  |   let fn; | ||
|  | 
 | ||
|  |   if (method === 'default') { | ||
|  |     fn = main.__esModule ? main['default'] : main; | ||
|  |   } else { | ||
|  |     fn = main[method]; | ||
|  |   } | ||
|  | 
 | ||
|  |   function execHelper() { | ||
|  |     execFunction(fn, main, args, reportSuccess, reportClientError); | ||
|  |   } | ||
|  | 
 | ||
|  |   if (initialized || !main.setup) { | ||
|  |     execHelper(); | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   initialized = true; | ||
|  |   execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError); | ||
|  | } | ||
|  | 
 | ||
|  | function execFunction(fn, ctx, args, onResult, onError) { | ||
|  |   let result; | ||
|  | 
 | ||
|  |   try { | ||
|  |     result = fn.apply(ctx, args); | ||
|  |   } catch (err) { | ||
|  |     onError(err); | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (result && typeof result.then === 'function') { | ||
|  |     result.then(onResult, onError); | ||
|  |   } else { | ||
|  |     onResult(result); | ||
|  |   } | ||
|  | } |