processChild.js 3.4 KB

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