process: split bootstrappers by threads that can run them
This patch split part of the bootstrappers into three files: - `lib/internal/process/main_thread_only.js`: contains bootstrappers that can only be run in the main thread, including - `setupStdio` for the main thread that sets up `process.stdin`, `process.stdout`, `process.error` that may interact with external resources, e.g. TTY/File/Pipe/TCP sockets - `setupProcessMethods` that setup methods changing process-global states, e.g. `process.chdir`, `process.umask`, `process.setuid` - `setupSignalHandlers` - `setupChildProcessIpcChannel` that setup `process.send` for child processes. - `lib/internal/process/worker_thread_only.js`: contains bootstrappers that can only be run in the worker threads, including - `setupStdio` for the worker thread that are streams to be manipulated or piped to the parent thread - `lib/internal/process/per_thread.js`: contains bootstrappers that can be run in all threads, including: - `setupAssert` for `process.assert` - `setupCpuUsage` for `process.cpuUsage` - `setupHrtime` for `process.hrtime` and `process.hrtime.bigint` - `setupMemoryUsage` for `process.memoryUsage` - `setupConfig` for `process.config` - `setupKillAndExit` for `process.kill` and `process.exit` - `setupRawDebug` for `process._rawDebug` - `setupUncaughtExceptionCapture` for `process.setUncaughtExceptionCaptureCallback` and `process.hasUncaughtExceptionCaptureCallback` Hopefully in the future we can sort more bootstrappers in `boostrap/node.js` into these three files and further group them into functions that can be run before creating the snapshot / after loading the snapshot. This patch also moves most of the `isMainThread` conditionals into the main bootstrapper instead of letting them scattered around special-casing different implementations. PR-URL: https://github.com/nodejs/node/pull/21378 Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This commit is contained in:
parent
420d8afe3d
commit
e43d91cdc1
@ -44,33 +44,57 @@
|
|||||||
|
|
||||||
setupGlobalVariables();
|
setupGlobalVariables();
|
||||||
|
|
||||||
const _process = NativeModule.require('internal/process');
|
// Bootstrappers for all threads, including worker threads and main thread
|
||||||
_process.setupConfig(NativeModule._source);
|
const perThreadSetup = NativeModule.require('internal/process/per_thread');
|
||||||
_process.setupSignalHandlers();
|
// Bootstrappers for the main thread only
|
||||||
_process.setupUncaughtExceptionCapture(exceptionHandlerState,
|
let mainThreadSetup;
|
||||||
_shouldAbortOnUncaughtToggle);
|
// Bootstrappers for the worker threads only
|
||||||
|
let workerThreadSetup;
|
||||||
|
if (isMainThread) {
|
||||||
|
mainThreadSetup = NativeModule.require(
|
||||||
|
'internal/process/main_thread_only'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
workerThreadSetup = NativeModule.require(
|
||||||
|
'internal/process/worker_thread_only'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
perThreadSetup.setupAssert();
|
||||||
|
perThreadSetup.setupConfig(NativeModule._source);
|
||||||
|
|
||||||
|
if (isMainThread) {
|
||||||
|
mainThreadSetup.setupSignalHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
perThreadSetup.setupUncaughtExceptionCapture(exceptionHandlerState,
|
||||||
|
_shouldAbortOnUncaughtToggle);
|
||||||
|
|
||||||
NativeModule.require('internal/process/warning').setup();
|
NativeModule.require('internal/process/warning').setup();
|
||||||
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
|
NativeModule.require('internal/process/next_tick').setup(_setupNextTick,
|
||||||
_setupPromises);
|
_setupPromises);
|
||||||
NativeModule.require('internal/process/stdio').setup();
|
|
||||||
NativeModule.require('internal/process/methods').setup(_chdir,
|
if (isMainThread) {
|
||||||
_umask,
|
mainThreadSetup.setupStdio();
|
||||||
_initgroups,
|
mainThreadSetup.setupProcessMethods(
|
||||||
_setegid,
|
_chdir, _umask, _initgroups, _setegid, _seteuid,
|
||||||
_seteuid,
|
_setgid, _setuid, _setgroups
|
||||||
_setgid,
|
);
|
||||||
_setuid,
|
} else {
|
||||||
_setgroups);
|
workerThreadSetup.setupStdio();
|
||||||
|
}
|
||||||
|
|
||||||
const perf = process.binding('performance');
|
const perf = process.binding('performance');
|
||||||
const {
|
const {
|
||||||
NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE,
|
NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE,
|
||||||
} = perf.constants;
|
} = perf.constants;
|
||||||
|
|
||||||
_process.setup_hrtime(_hrtime, _hrtimeBigInt);
|
perThreadSetup.setupRawDebug(_rawDebug);
|
||||||
_process.setup_cpuUsage(_cpuUsage);
|
perThreadSetup.setupHrtime(_hrtime, _hrtimeBigInt);
|
||||||
_process.setupMemoryUsage(_memoryUsage);
|
perThreadSetup.setupCpuUsage(_cpuUsage);
|
||||||
_process.setupKillAndExit();
|
perThreadSetup.setupMemoryUsage(_memoryUsage);
|
||||||
|
perThreadSetup.setupKillAndExit();
|
||||||
|
|
||||||
if (global.__coverage__)
|
if (global.__coverage__)
|
||||||
NativeModule.require('internal/process/write-coverage').setup();
|
NativeModule.require('internal/process/write-coverage').setup();
|
||||||
|
|
||||||
@ -90,10 +114,9 @@
|
|||||||
NativeModule.require('internal/inspector_async_hook').setup();
|
NativeModule.require('internal/inspector_async_hook').setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMainThread)
|
if (isMainThread) {
|
||||||
_process.setupChannel();
|
mainThreadSetup.setupChildProcessIpcChannel();
|
||||||
|
}
|
||||||
_process.setupRawDebug(_rawDebug);
|
|
||||||
|
|
||||||
const browserGlobals = !process._noBrowserGlobals;
|
const browserGlobals = !process._noBrowserGlobals;
|
||||||
if (browserGlobals) {
|
if (browserGlobals) {
|
||||||
|
@ -1,24 +1,35 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// This file contains process bootstrappers that can only be
|
||||||
|
// run in the main thread
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ERR_INVALID_ARG_TYPE,
|
errnoException,
|
||||||
ERR_UNKNOWN_CREDENTIAL
|
codes: {
|
||||||
} = require('internal/errors').codes;
|
ERR_INVALID_ARG_TYPE,
|
||||||
|
ERR_UNKNOWN_CREDENTIAL
|
||||||
|
}
|
||||||
|
} = require('internal/errors');
|
||||||
const {
|
const {
|
||||||
validateMode,
|
validateMode,
|
||||||
validateUint32
|
validateUint32
|
||||||
} = require('internal/validators');
|
} = require('internal/validators');
|
||||||
const {
|
|
||||||
isMainThread
|
|
||||||
} = require('internal/worker');
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
setupProcessStdio,
|
||||||
|
getMainThreadStdio
|
||||||
|
} = require('internal/process/stdio');
|
||||||
|
|
||||||
|
const assert = require('assert').strict;
|
||||||
|
|
||||||
|
function setupStdio() {
|
||||||
|
setupProcessStdio(getMainThreadStdio());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-POSIX platforms like Windows don't have certain methods.
|
||||||
|
// Workers also lack these methods since they change process-global state.
|
||||||
function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
function setupProcessMethods(_chdir, _umask, _initgroups, _setegid,
|
||||||
_seteuid, _setgid, _setuid, _setgroups) {
|
_seteuid, _setgid, _setuid, _setgroups) {
|
||||||
// Non-POSIX platforms like Windows don't have certain methods.
|
|
||||||
// Workers also lack these methods since they change process-global state.
|
|
||||||
if (!isMainThread)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (_setgid !== undefined) {
|
if (_setgid !== undefined) {
|
||||||
setupPosixMethods(_initgroups, _setegid, _seteuid,
|
setupPosixMethods(_initgroups, _setegid, _seteuid,
|
||||||
_setgid, _setuid, _setgroups);
|
_setgid, _setuid, _setgroups);
|
||||||
@ -105,4 +116,64 @@ function setupPosixMethods(_initgroups, _setegid, _seteuid,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.setup = setupProcessMethods;
|
// Worker threads don't receive signals.
|
||||||
|
function setupSignalHandlers() {
|
||||||
|
const constants = process.binding('constants').os.signals;
|
||||||
|
const signalWraps = Object.create(null);
|
||||||
|
let Signal;
|
||||||
|
|
||||||
|
function isSignal(event) {
|
||||||
|
return typeof event === 'string' && constants[event] !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect presence of a listener for the special signal types
|
||||||
|
process.on('newListener', function(type) {
|
||||||
|
if (isSignal(type) && signalWraps[type] === undefined) {
|
||||||
|
if (Signal === undefined)
|
||||||
|
Signal = process.binding('signal_wrap').Signal;
|
||||||
|
const wrap = new Signal();
|
||||||
|
|
||||||
|
wrap.unref();
|
||||||
|
|
||||||
|
wrap.onsignal = process.emit.bind(process, type, type);
|
||||||
|
|
||||||
|
const signum = constants[type];
|
||||||
|
const err = wrap.start(signum);
|
||||||
|
if (err) {
|
||||||
|
wrap.close();
|
||||||
|
throw errnoException(err, 'uv_signal_start');
|
||||||
|
}
|
||||||
|
|
||||||
|
signalWraps[type] = wrap;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on('removeListener', function(type) {
|
||||||
|
if (signalWraps[type] !== undefined && this.listenerCount(type) === 0) {
|
||||||
|
signalWraps[type].close();
|
||||||
|
delete signalWraps[type];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupChildProcessIpcChannel() {
|
||||||
|
// If we were spawned with env NODE_CHANNEL_FD then load that up and
|
||||||
|
// start parsing data from that stream.
|
||||||
|
if (process.env.NODE_CHANNEL_FD) {
|
||||||
|
const fd = parseInt(process.env.NODE_CHANNEL_FD, 10);
|
||||||
|
assert(fd >= 0);
|
||||||
|
|
||||||
|
// Make sure it's not accidentally inherited by child processes.
|
||||||
|
delete process.env.NODE_CHANNEL_FD;
|
||||||
|
|
||||||
|
require('child_process')._forkChild(fd);
|
||||||
|
assert(process.send);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setupStdio,
|
||||||
|
setupProcessMethods,
|
||||||
|
setupSignalHandlers,
|
||||||
|
setupChildProcessIpcChannel
|
||||||
|
};
|
@ -1,5 +1,9 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
// This files contains process bootstrappers that can be
|
||||||
|
// run when setting up each thread, including the main
|
||||||
|
// thread and the worker threads.
|
||||||
|
|
||||||
const {
|
const {
|
||||||
errnoException,
|
errnoException,
|
||||||
codes: {
|
codes: {
|
||||||
@ -14,19 +18,19 @@ const {
|
|||||||
} = require('internal/errors');
|
} = require('internal/errors');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
const constants = process.binding('constants').os.signals;
|
const constants = process.binding('constants').os.signals;
|
||||||
const assert = require('assert').strict;
|
|
||||||
const { deprecate } = require('internal/util');
|
const { deprecate } = require('internal/util');
|
||||||
const { isMainThread } = require('internal/worker');
|
|
||||||
|
|
||||||
process.assert = deprecate(
|
function setupAssert() {
|
||||||
function(x, msg) {
|
process.assert = deprecate(
|
||||||
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
|
function(x, msg) {
|
||||||
},
|
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
|
||||||
'process.assert() is deprecated. Please use the `assert` module instead.',
|
},
|
||||||
'DEP0100');
|
'process.assert() is deprecated. Please use the `assert` module instead.',
|
||||||
|
'DEP0100');
|
||||||
|
}
|
||||||
|
|
||||||
// Set up the process.cpuUsage() function.
|
// Set up the process.cpuUsage() function.
|
||||||
function setup_cpuUsage(_cpuUsage) {
|
function setupCpuUsage(_cpuUsage) {
|
||||||
// Create the argument array that will be passed to the native function.
|
// Create the argument array that will be passed to the native function.
|
||||||
const cpuValues = new Float64Array(2);
|
const cpuValues = new Float64Array(2);
|
||||||
|
|
||||||
@ -90,7 +94,7 @@ function setup_cpuUsage(_cpuUsage) {
|
|||||||
// The 3 entries filled in by the original process.hrtime contains
|
// The 3 entries filled in by the original process.hrtime contains
|
||||||
// the upper/lower 32 bits of the second part of the value,
|
// the upper/lower 32 bits of the second part of the value,
|
||||||
// and the remaining nanoseconds of the value.
|
// and the remaining nanoseconds of the value.
|
||||||
function setup_hrtime(_hrtime, _hrtimeBigInt) {
|
function setupHrtime(_hrtime, _hrtimeBigInt) {
|
||||||
const hrValues = new Uint32Array(3);
|
const hrValues = new Uint32Array(3);
|
||||||
|
|
||||||
process.hrtime = function hrtime(time) {
|
process.hrtime = function hrtime(time) {
|
||||||
@ -193,67 +197,6 @@ function setupKillAndExit() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function setupSignalHandlers() {
|
|
||||||
if (!isMainThread) {
|
|
||||||
// Worker threads don't receive signals.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const signalWraps = Object.create(null);
|
|
||||||
let Signal;
|
|
||||||
|
|
||||||
function isSignal(event) {
|
|
||||||
return typeof event === 'string' && constants[event] !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect presence of a listener for the special signal types
|
|
||||||
process.on('newListener', function(type) {
|
|
||||||
if (isSignal(type) && signalWraps[type] === undefined) {
|
|
||||||
if (Signal === undefined)
|
|
||||||
Signal = process.binding('signal_wrap').Signal;
|
|
||||||
const wrap = new Signal();
|
|
||||||
|
|
||||||
wrap.unref();
|
|
||||||
|
|
||||||
wrap.onsignal = process.emit.bind(process, type, type);
|
|
||||||
|
|
||||||
const signum = constants[type];
|
|
||||||
const err = wrap.start(signum);
|
|
||||||
if (err) {
|
|
||||||
wrap.close();
|
|
||||||
throw errnoException(err, 'uv_signal_start');
|
|
||||||
}
|
|
||||||
|
|
||||||
signalWraps[type] = wrap;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
process.on('removeListener', function(type) {
|
|
||||||
if (signalWraps[type] !== undefined && this.listenerCount(type) === 0) {
|
|
||||||
signalWraps[type].close();
|
|
||||||
delete signalWraps[type];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setupChannel() {
|
|
||||||
// If we were spawned with env NODE_CHANNEL_FD then load that up and
|
|
||||||
// start parsing data from that stream.
|
|
||||||
if (process.env.NODE_CHANNEL_FD) {
|
|
||||||
const fd = parseInt(process.env.NODE_CHANNEL_FD, 10);
|
|
||||||
assert(fd >= 0);
|
|
||||||
|
|
||||||
// Make sure it's not accidentally inherited by child processes.
|
|
||||||
delete process.env.NODE_CHANNEL_FD;
|
|
||||||
|
|
||||||
require('child_process')._forkChild(fd);
|
|
||||||
assert(process.send);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function setupRawDebug(_rawDebug) {
|
function setupRawDebug(_rawDebug) {
|
||||||
process._rawDebug = function() {
|
process._rawDebug = function() {
|
||||||
_rawDebug(util.format.apply(null, arguments));
|
_rawDebug(util.format.apply(null, arguments));
|
||||||
@ -288,13 +231,12 @@ function setupUncaughtExceptionCapture(exceptionHandlerState,
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
setup_cpuUsage,
|
setupAssert,
|
||||||
setup_hrtime,
|
setupCpuUsage,
|
||||||
|
setupHrtime,
|
||||||
setupMemoryUsage,
|
setupMemoryUsage,
|
||||||
setupConfig,
|
setupConfig,
|
||||||
setupKillAndExit,
|
setupKillAndExit,
|
||||||
setupSignalHandlers,
|
|
||||||
setupChannel,
|
|
||||||
setupRawDebug,
|
setupRawDebug,
|
||||||
setupUncaughtExceptionCapture
|
setupUncaughtExceptionCapture
|
||||||
};
|
};
|
@ -6,21 +6,17 @@ const {
|
|||||||
ERR_UNKNOWN_STDIN_TYPE,
|
ERR_UNKNOWN_STDIN_TYPE,
|
||||||
ERR_UNKNOWN_STREAM_TYPE
|
ERR_UNKNOWN_STREAM_TYPE
|
||||||
} = require('internal/errors').codes;
|
} = require('internal/errors').codes;
|
||||||
const {
|
|
||||||
isMainThread,
|
|
||||||
workerStdio
|
|
||||||
} = require('internal/worker');
|
|
||||||
|
|
||||||
exports.setup = setupStdio;
|
exports.setupProcessStdio = setupProcessStdio;
|
||||||
|
exports.getMainThreadStdio = getMainThreadStdio;
|
||||||
|
|
||||||
function setupStdio() {
|
function getMainThreadStdio() {
|
||||||
var stdin;
|
var stdin;
|
||||||
var stdout;
|
var stdout;
|
||||||
var stderr;
|
var stderr;
|
||||||
|
|
||||||
function getStdout() {
|
function getStdout() {
|
||||||
if (stdout) return stdout;
|
if (stdout) return stdout;
|
||||||
if (!isMainThread) return workerStdio.stdout;
|
|
||||||
stdout = createWritableStdioStream(1);
|
stdout = createWritableStdioStream(1);
|
||||||
stdout.destroySoon = stdout.destroy;
|
stdout.destroySoon = stdout.destroy;
|
||||||
stdout._destroy = function(er, cb) {
|
stdout._destroy = function(er, cb) {
|
||||||
@ -36,7 +32,6 @@ function setupStdio() {
|
|||||||
|
|
||||||
function getStderr() {
|
function getStderr() {
|
||||||
if (stderr) return stderr;
|
if (stderr) return stderr;
|
||||||
if (!isMainThread) return workerStdio.stderr;
|
|
||||||
stderr = createWritableStdioStream(2);
|
stderr = createWritableStdioStream(2);
|
||||||
stderr.destroySoon = stderr.destroy;
|
stderr.destroySoon = stderr.destroy;
|
||||||
stderr._destroy = function(er, cb) {
|
stderr._destroy = function(er, cb) {
|
||||||
@ -52,8 +47,6 @@ function setupStdio() {
|
|||||||
|
|
||||||
function getStdin() {
|
function getStdin() {
|
||||||
if (stdin) return stdin;
|
if (stdin) return stdin;
|
||||||
if (!isMainThread) return workerStdio.stdin;
|
|
||||||
|
|
||||||
const tty_wrap = process.binding('tty_wrap');
|
const tty_wrap = process.binding('tty_wrap');
|
||||||
const fd = 0;
|
const fd = 0;
|
||||||
|
|
||||||
@ -136,6 +129,14 @@ function setupStdio() {
|
|||||||
return stdin;
|
return stdin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getStdout,
|
||||||
|
getStderr,
|
||||||
|
getStdin
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupProcessStdio({ getStdout, getStdin, getStderr }) {
|
||||||
Object.defineProperty(process, 'stdout', {
|
Object.defineProperty(process, 'stdout', {
|
||||||
configurable: true,
|
configurable: true,
|
||||||
enumerable: true,
|
enumerable: true,
|
||||||
|
24
lib/internal/process/worker_thread_only.js
Normal file
24
lib/internal/process/worker_thread_only.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
// This file contains process bootstrappers that can only be
|
||||||
|
// run in the worker thread.
|
||||||
|
|
||||||
|
const {
|
||||||
|
setupProcessStdio
|
||||||
|
} = require('internal/process/stdio');
|
||||||
|
|
||||||
|
const {
|
||||||
|
workerStdio
|
||||||
|
} = require('internal/worker');
|
||||||
|
|
||||||
|
function setupStdio() {
|
||||||
|
setupProcessStdio({
|
||||||
|
getStdout: () => workerStdio.stdout,
|
||||||
|
getStderr: () => workerStdio.stderr,
|
||||||
|
getStdin: () => workerStdio.stdin
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setupStdio
|
||||||
|
};
|
5
node.gyp
5
node.gyp
@ -128,12 +128,13 @@
|
|||||||
'lib/internal/os.js',
|
'lib/internal/os.js',
|
||||||
'lib/internal/priority_queue.js',
|
'lib/internal/priority_queue.js',
|
||||||
'lib/internal/process/esm_loader.js',
|
'lib/internal/process/esm_loader.js',
|
||||||
'lib/internal/process/methods.js',
|
'lib/internal/process/main_thread_only.js',
|
||||||
'lib/internal/process/next_tick.js',
|
'lib/internal/process/next_tick.js',
|
||||||
|
'lib/internal/process/per_thread.js',
|
||||||
'lib/internal/process/promises.js',
|
'lib/internal/process/promises.js',
|
||||||
'lib/internal/process/stdio.js',
|
'lib/internal/process/stdio.js',
|
||||||
'lib/internal/process/warning.js',
|
'lib/internal/process/warning.js',
|
||||||
'lib/internal/process.js',
|
'lib/internal/process/worker_thread_only.js',
|
||||||
'lib/internal/querystring.js',
|
'lib/internal/querystring.js',
|
||||||
'lib/internal/process/write-coverage.js',
|
'lib/internal/process/write-coverage.js',
|
||||||
'lib/internal/readline.js',
|
'lib/internal/readline.js',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user