threadChild.js 3.6 KB

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