threadChild.js 3.7 KB

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