mirror of
https://github.com/nodejs/node.git
synced 2024-12-01 16:10:02 +01:00
a33c3c6d33
The C++ land `node::FatalException()` is not in fact fatal anymore.
It gives the user a chance to handle the uncaught exception
globally by listening to the `uncaughtException` event. This patch
renames it to `TriggerUncaughtException` in C++ to avoid the confusion.
In addition rename the JS land handler to `onGlobalUncaughtException`
to reflect its purpose - we have to keep the alias
`process._fatalException` and use that for now since it has been
monkey-patchable in the user land.
This patch also
- Adds more comments to the global uncaught exception handling routine
- Puts a few other C++ error handling functions into the `errors`
namespace
- Moves error-handling-related bindings to the `errors` binding.
Refs: 2b252acea4
PR-URL: https://github.com/nodejs/node/pull/28257
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
198 lines
5.3 KiB
JavaScript
198 lines
5.3 KiB
JavaScript
'use strict';
|
|
|
|
// In worker threads, execute the script sent through the
|
|
// message port.
|
|
|
|
const { Object } = primordials;
|
|
|
|
const {
|
|
patchProcessObject,
|
|
setupCoverageHooks,
|
|
setupInspectorHooks,
|
|
setupWarningHandler,
|
|
setupDebugEnv,
|
|
initializeDeprecations,
|
|
initializeCJSLoader,
|
|
initializeESMLoader,
|
|
initializeFrozenIntrinsics,
|
|
initializeReport,
|
|
loadPreloadModules,
|
|
setupTraceCategoryState
|
|
} = require('internal/bootstrap/pre_execution');
|
|
|
|
const {
|
|
threadId,
|
|
getEnvMessagePort
|
|
} = internalBinding('worker');
|
|
|
|
const workerIo = require('internal/worker/io');
|
|
const {
|
|
messageTypes: {
|
|
// Messages that may be received by workers
|
|
LOAD_SCRIPT,
|
|
// Messages that may be posted from workers
|
|
UP_AND_RUNNING,
|
|
ERROR_MESSAGE,
|
|
COULD_NOT_SERIALIZE_ERROR,
|
|
// Messages that may be either received or posted
|
|
STDIO_PAYLOAD,
|
|
STDIO_WANTS_MORE_DATA,
|
|
},
|
|
kStdioWantsMoreDataCallback
|
|
} = workerIo;
|
|
|
|
const {
|
|
onGlobalUncaughtException
|
|
} = require('internal/process/execution');
|
|
|
|
const publicWorker = require('worker_threads');
|
|
const debug = require('internal/util/debuglog').debuglog('worker');
|
|
|
|
const assert = require('internal/assert');
|
|
|
|
patchProcessObject();
|
|
setupInspectorHooks();
|
|
setupDebugEnv();
|
|
|
|
setupWarningHandler();
|
|
|
|
// Since worker threads cannot switch cwd, we do not need to
|
|
// overwrite the process.env.NODE_V8_COVERAGE variable.
|
|
if (process.env.NODE_V8_COVERAGE) {
|
|
setupCoverageHooks(process.env.NODE_V8_COVERAGE);
|
|
}
|
|
|
|
debug(`[${threadId}] is setting up worker child environment`);
|
|
|
|
// Set up the message port and start listening
|
|
const port = getEnvMessagePort();
|
|
|
|
// If the main thread is spawned with env NODE_CHANNEL_FD, it's probably
|
|
// spawned by our child_process module. In the work threads, mark the
|
|
// related IPC properties as unavailable.
|
|
if (process.env.NODE_CHANNEL_FD) {
|
|
const workerThreadSetup = require('internal/process/worker_thread_only');
|
|
Object.defineProperty(process, 'channel', {
|
|
enumerable: false,
|
|
get: workerThreadSetup.unavailable('process.channel')
|
|
});
|
|
|
|
Object.defineProperty(process, 'connected', {
|
|
enumerable: false,
|
|
get: workerThreadSetup.unavailable('process.connected')
|
|
});
|
|
|
|
process.send = workerThreadSetup.unavailable('process.send()');
|
|
process.disconnect =
|
|
workerThreadSetup.unavailable('process.disconnect()');
|
|
}
|
|
|
|
port.on('message', (message) => {
|
|
if (message.type === LOAD_SCRIPT) {
|
|
const {
|
|
cwdCounter,
|
|
filename,
|
|
doEval,
|
|
workerData,
|
|
publicPort,
|
|
manifestSrc,
|
|
manifestURL,
|
|
hasStdin
|
|
} = message;
|
|
|
|
setupTraceCategoryState();
|
|
initializeReport();
|
|
if (manifestSrc) {
|
|
require('internal/process/policy').setup(manifestSrc, manifestURL);
|
|
}
|
|
initializeDeprecations();
|
|
initializeFrozenIntrinsics();
|
|
initializeCJSLoader();
|
|
initializeESMLoader();
|
|
loadPreloadModules();
|
|
publicWorker.parentPort = publicPort;
|
|
publicWorker.workerData = workerData;
|
|
|
|
// The counter is only passed to the workers created by the main thread, not
|
|
// to workers created by other workers.
|
|
let cachedCwd = '';
|
|
let lastCounter = -1;
|
|
const originalCwd = process.cwd;
|
|
|
|
process.cwd = function() {
|
|
const currentCounter = Atomics.load(cwdCounter, 0);
|
|
if (currentCounter === lastCounter)
|
|
return cachedCwd;
|
|
lastCounter = currentCounter;
|
|
cachedCwd = originalCwd();
|
|
return cachedCwd;
|
|
};
|
|
workerIo.sharedCwdCounter = cwdCounter;
|
|
|
|
if (!hasStdin)
|
|
process.stdin.push(null);
|
|
|
|
debug(`[${threadId}] starts worker script ${filename} ` +
|
|
`(eval = ${eval}) at cwd = ${process.cwd()}`);
|
|
port.unref();
|
|
port.postMessage({ type: UP_AND_RUNNING });
|
|
if (doEval) {
|
|
const { evalScript } = require('internal/process/execution');
|
|
evalScript('[worker eval]', filename);
|
|
} else {
|
|
process.argv[1] = filename; // script filename
|
|
require('module').runMain();
|
|
}
|
|
} else if (message.type === STDIO_PAYLOAD) {
|
|
const { stream, chunk, encoding } = message;
|
|
process[stream].push(chunk, encoding);
|
|
} else {
|
|
assert(
|
|
message.type === STDIO_WANTS_MORE_DATA,
|
|
`Unknown worker message type ${message.type}`
|
|
);
|
|
const { stream } = message;
|
|
process[stream][kStdioWantsMoreDataCallback]();
|
|
}
|
|
});
|
|
|
|
function workerOnGlobalUncaughtException(error, fromPromise) {
|
|
debug(`[${threadId}] gets uncaught exception`);
|
|
let handled = false;
|
|
try {
|
|
handled = onGlobalUncaughtException(error, fromPromise);
|
|
} catch (e) {
|
|
error = e;
|
|
}
|
|
debug(`[${threadId}] uncaught exception handled = ${handled}`);
|
|
|
|
if (!handled) {
|
|
let serialized;
|
|
try {
|
|
const { serializeError } = require('internal/error-serdes');
|
|
serialized = serializeError(error);
|
|
} catch {}
|
|
debug(`[${threadId}] uncaught exception serialized = ${!!serialized}`);
|
|
if (serialized)
|
|
port.postMessage({
|
|
type: ERROR_MESSAGE,
|
|
error: serialized
|
|
});
|
|
else
|
|
port.postMessage({ type: COULD_NOT_SERIALIZE_ERROR });
|
|
|
|
const { clearAsyncIdStack } = require('internal/async_hooks');
|
|
clearAsyncIdStack();
|
|
|
|
process.exit();
|
|
}
|
|
}
|
|
|
|
// Patch the global uncaught exception handler so it gets picked up by
|
|
// node::errors::TriggerUncaughtException().
|
|
process._fatalException = workerOnGlobalUncaughtException;
|
|
|
|
markBootstrapComplete();
|
|
|
|
port.start();
|