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:
Joyee Cheung 2018-06-18 00:40:58 +08:00
parent 420d8afe3d
commit e43d91cdc1
No known key found for this signature in database
GPG Key ID: 92B78A53C8303B8D
6 changed files with 183 additions and 121 deletions

View File

@ -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) {

View File

@ -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
};

View File

@ -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
}; };

View File

@ -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,

View 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
};

View File

@ -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',