| 'use strict'; |
| |
| function _types() { |
| const data = require('../types'); |
| |
| _types = function () { |
| return data; |
| }; |
| |
| return data; |
| } |
| |
| /** |
| * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. |
| * |
| * This source code is licensed under the MIT license found in the |
| * LICENSE file in the root directory of this source tree. |
| */ |
| 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. |
| */ |
| |
| const messageListener = 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] |
| ); |
| } |
| }; |
| |
| process.on('message', messageListener); |
| |
| 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' ? {...error} : error |
| ]); |
| } |
| |
| function end() { |
| const main = require(file); |
| |
| if (!main.teardown) { |
| exitProcess(); |
| return; |
| } |
| |
| execFunction(main.teardown, main, [], exitProcess, exitProcess); |
| } |
| |
| function exitProcess() { |
| // Clean up open handles so the process ideally exits gracefully |
| process.removeListener('message', messageListener); |
| } |
| |
| 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); |
| } |
| |
| const isPromise = obj => |
| !!obj && |
| (typeof obj === 'object' || typeof obj === 'function') && |
| typeof obj.then === 'function'; |
| |
| function execFunction(fn, ctx, args, onResult, onError) { |
| let result; |
| |
| try { |
| result = fn.apply(ctx, args); |
| } catch (err) { |
| onError(err); |
| return; |
| } |
| |
| if (isPromise(result)) { |
| result.then(onResult, onError); |
| } else { |
| onResult(result); |
| } |
| } |