processChild.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. 'use strict';
  2. var _types = require('../types');
  3. /**
  4. * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
  5. *
  6. * This source code is licensed under the MIT license found in the
  7. * LICENSE file in the root directory of this source tree.
  8. */
  9. let file = null;
  10. let setupArgs = [];
  11. let initialized = false;
  12. /**
  13. * This file is a small bootstrapper for workers. It sets up the communication
  14. * between the worker and the parent process, interpreting parent messages and
  15. * sending results back.
  16. *
  17. * The file loaded will be lazily initialized the first time any of the workers
  18. * is called. This is done for optimal performance: if the farm is initialized,
  19. * but no call is made to it, child Node processes will be consuming the least
  20. * possible amount of memory.
  21. *
  22. * If an invalid message is detected, the child will exit (by throwing) with a
  23. * non-zero exit code.
  24. */
  25. const messageListener = request => {
  26. switch (request[0]) {
  27. case _types.CHILD_MESSAGE_INITIALIZE:
  28. const init = request;
  29. file = init[2];
  30. setupArgs = request[3];
  31. break;
  32. case _types.CHILD_MESSAGE_CALL:
  33. const call = request;
  34. execMethod(call[2], call[3]);
  35. break;
  36. case _types.CHILD_MESSAGE_END:
  37. end();
  38. break;
  39. default:
  40. throw new TypeError(
  41. 'Unexpected request from parent process: ' + request[0]
  42. );
  43. }
  44. };
  45. process.on('message', messageListener);
  46. function reportSuccess(result) {
  47. if (!process || !process.send) {
  48. throw new Error('Child can only be used on a forked process');
  49. }
  50. process.send([_types.PARENT_MESSAGE_OK, result]);
  51. }
  52. function reportClientError(error) {
  53. return reportError(error, _types.PARENT_MESSAGE_CLIENT_ERROR);
  54. }
  55. function reportInitializeError(error) {
  56. return reportError(error, _types.PARENT_MESSAGE_SETUP_ERROR);
  57. }
  58. function reportError(error, type) {
  59. if (!process || !process.send) {
  60. throw new Error('Child can only be used on a forked process');
  61. }
  62. if (error == null) {
  63. error = new Error('"null" or "undefined" thrown');
  64. }
  65. process.send([
  66. type,
  67. error.constructor && error.constructor.name,
  68. error.message,
  69. error.stack,
  70. typeof error === 'object' ? {...error} : error
  71. ]);
  72. }
  73. function end() {
  74. const main = require(file);
  75. if (!main.teardown) {
  76. exitProcess();
  77. return;
  78. }
  79. execFunction(main.teardown, main, [], exitProcess, exitProcess);
  80. }
  81. function exitProcess() {
  82. // Clean up open handles so the process ideally exits gracefully
  83. process.removeListener('message', messageListener);
  84. }
  85. function execMethod(method, args) {
  86. const main = require(file);
  87. let fn;
  88. if (method === 'default') {
  89. fn = main.__esModule ? main['default'] : main;
  90. } else {
  91. fn = main[method];
  92. }
  93. function execHelper() {
  94. execFunction(fn, main, args, reportSuccess, reportClientError);
  95. }
  96. if (initialized || !main.setup) {
  97. execHelper();
  98. return;
  99. }
  100. initialized = true;
  101. execFunction(main.setup, main, setupArgs, execHelper, reportInitializeError);
  102. }
  103. const isPromise = obj =>
  104. !!obj &&
  105. (typeof obj === 'object' || typeof obj === 'function') &&
  106. typeof obj.then === 'function';
  107. function execFunction(fn, ctx, args, onResult, onError) {
  108. let result;
  109. try {
  110. result = fn.apply(ctx, args);
  111. } catch (err) {
  112. onError(err);
  113. return;
  114. }
  115. if (isPromise(result)) {
  116. result.then(onResult, onError);
  117. } else {
  118. onResult(result);
  119. }
  120. }