• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1'use strict';
2
3// In worker threads, execute the script sent through the
4// message port.
5
6const {
7  ObjectDefineProperty,
8} = primordials;
9
10const {
11  patchProcessObject,
12  setupCoverageHooks,
13  setupInspectorHooks,
14  setupWarningHandler,
15  setupDebugEnv,
16  initializeDeprecations,
17  initializeWASI,
18  initializeCJSLoader,
19  initializeESMLoader,
20  initializeFrozenIntrinsics,
21  initializeReport,
22  loadPreloadModules,
23  setupTraceCategoryState
24} = require('internal/bootstrap/pre_execution');
25
26const {
27  threadId,
28  getEnvMessagePort
29} = internalBinding('worker');
30
31const workerIo = require('internal/worker/io');
32const {
33  messageTypes: {
34    // Messages that may be received by workers
35    LOAD_SCRIPT,
36    // Messages that may be posted from workers
37    UP_AND_RUNNING,
38    ERROR_MESSAGE,
39    COULD_NOT_SERIALIZE_ERROR,
40    // Messages that may be either received or posted
41    STDIO_PAYLOAD,
42    STDIO_WANTS_MORE_DATA,
43  },
44  kStdioWantsMoreDataCallback
45} = workerIo;
46
47const {
48  onGlobalUncaughtException
49} = require('internal/process/execution');
50
51const publicWorker = require('worker_threads');
52let debug = require('internal/util/debuglog').debuglog('worker', (fn) => {
53  debug = fn;
54});
55
56const assert = require('internal/assert');
57
58patchProcessObject();
59setupInspectorHooks();
60setupDebugEnv();
61
62setupWarningHandler();
63
64// Since worker threads cannot switch cwd, we do not need to
65// overwrite the process.env.NODE_V8_COVERAGE variable.
66if (process.env.NODE_V8_COVERAGE) {
67  setupCoverageHooks(process.env.NODE_V8_COVERAGE);
68}
69
70debug(`[${threadId}] is setting up worker child environment`);
71
72// Set up the message port and start listening
73const port = getEnvMessagePort();
74
75// If the main thread is spawned with env NODE_CHANNEL_FD, it's probably
76// spawned by our child_process module. In the work threads, mark the
77// related IPC properties as unavailable.
78if (process.env.NODE_CHANNEL_FD) {
79  const workerThreadSetup = require('internal/process/worker_thread_only');
80  ObjectDefineProperty(process, 'channel', {
81    enumerable: false,
82    get: workerThreadSetup.unavailable('process.channel')
83  });
84
85  ObjectDefineProperty(process, 'connected', {
86    enumerable: false,
87    get: workerThreadSetup.unavailable('process.connected')
88  });
89
90  process.send = workerThreadSetup.unavailable('process.send()');
91  process.disconnect =
92    workerThreadSetup.unavailable('process.disconnect()');
93}
94
95port.on('message', (message) => {
96  if (message.type === LOAD_SCRIPT) {
97    port.unref();
98    const {
99      argv,
100      cwdCounter,
101      filename,
102      doEval,
103      workerData,
104      publicPort,
105      manifestSrc,
106      manifestURL,
107      hasStdin
108    } = message;
109
110    setupTraceCategoryState();
111    initializeReport();
112    if (manifestSrc) {
113      require('internal/process/policy').setup(manifestSrc, manifestURL);
114    }
115    initializeDeprecations();
116    initializeWASI();
117    initializeCJSLoader();
118    initializeESMLoader();
119
120    const CJSLoader = require('internal/modules/cjs/loader');
121    assert(!CJSLoader.hasLoadedAnyUserCJSModule);
122    loadPreloadModules();
123    initializeFrozenIntrinsics();
124    if (argv !== undefined) {
125      process.argv = process.argv.concat(argv);
126    }
127    publicWorker.parentPort = publicPort;
128    publicWorker.workerData = workerData;
129
130    // The counter is only passed to the workers created by the main thread, not
131    // to workers created by other workers.
132    let cachedCwd = '';
133    const originalCwd = process.cwd;
134
135    process.cwd = function() {
136      cachedCwd = originalCwd();
137      return cachedCwd;
138    };
139    workerIo.sharedCwdCounter = cwdCounter;
140
141    if (!hasStdin)
142      process.stdin.push(null);
143
144    debug(`[${threadId}] starts worker script ${filename} ` +
145          `(eval = ${eval}) at cwd = ${process.cwd()}`);
146    port.postMessage({ type: UP_AND_RUNNING });
147    if (doEval) {
148      const { evalScript } = require('internal/process/execution');
149      const name = '[worker eval]';
150      // This is necessary for CJS module compilation.
151      // TODO: pass this with something really internal.
152      ObjectDefineProperty(process, '_eval', {
153        configurable: true,
154        enumerable: true,
155        value: filename,
156      });
157      process.argv.splice(1, 0, name);
158      evalScript(name, filename);
159    } else {
160      // script filename
161      // runMain here might be monkey-patched by users in --require.
162      // XXX: the monkey-patchability here should probably be deprecated.
163      process.argv.splice(1, 0, filename);
164      CJSLoader.Module.runMain(filename);
165    }
166  } else if (message.type === STDIO_PAYLOAD) {
167    const { stream, chunks } = message;
168    for (const { chunk, encoding } of chunks)
169      process[stream].push(chunk, encoding);
170  } else {
171    assert(
172      message.type === STDIO_WANTS_MORE_DATA,
173      `Unknown worker message type ${message.type}`
174    );
175    const { stream } = message;
176    process[stream][kStdioWantsMoreDataCallback]();
177  }
178});
179
180function workerOnGlobalUncaughtException(error, fromPromise) {
181  debug(`[${threadId}] gets uncaught exception`);
182  let handled = false;
183  try {
184    handled = onGlobalUncaughtException(error, fromPromise);
185  } catch (e) {
186    error = e;
187  }
188  debug(`[${threadId}] uncaught exception handled = ${handled}`);
189
190  if (handled) {
191    return true;
192  }
193
194  let serialized;
195  try {
196    const { serializeError } = require('internal/error_serdes');
197    serialized = serializeError(error);
198  } catch {}
199  debug(`[${threadId}] uncaught exception serialized = ${!!serialized}`);
200  if (serialized)
201    port.postMessage({
202      type: ERROR_MESSAGE,
203      error: serialized
204    });
205  else
206    port.postMessage({ type: COULD_NOT_SERIALIZE_ERROR });
207
208  const { clearAsyncIdStack } = require('internal/async_hooks');
209  clearAsyncIdStack();
210
211  process.exit();
212}
213
214// Patch the global uncaught exception handler so it gets picked up by
215// node::errors::TriggerUncaughtException().
216process._fatalException = workerOnGlobalUncaughtException;
217
218markBootstrapComplete();
219
220port.start();
221