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();
const _process = NativeModule.require('internal/process');
_process.setupConfig(NativeModule._source);
_process.setupSignalHandlers();
_process.setupUncaughtExceptionCapture(exceptionHandlerState,
_shouldAbortOnUncaughtToggle);
// Bootstrappers for all threads, including worker threads and main thread
const perThreadSetup = NativeModule.require('internal/process/per_thread');
// Bootstrappers for the main thread only
let mainThreadSetup;
// 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/next_tick').setup(_setupNextTick,
_setupPromises);
NativeModule.require('internal/process/stdio').setup();
NativeModule.require('internal/process/methods').setup(_chdir,
_umask,
_initgroups,
_setegid,
_seteuid,
_setgid,
_setuid,
_setgroups);
if (isMainThread) {
mainThreadSetup.setupStdio();
mainThreadSetup.setupProcessMethods(
_chdir, _umask, _initgroups, _setegid, _seteuid,
_setgid, _setuid, _setgroups
);
} else {
workerThreadSetup.setupStdio();
}
const perf = process.binding('performance');
const {
NODE_PERFORMANCE_MILESTONE_BOOTSTRAP_COMPLETE,
} = perf.constants;
_process.setup_hrtime(_hrtime, _hrtimeBigInt);
_process.setup_cpuUsage(_cpuUsage);
_process.setupMemoryUsage(_memoryUsage);
_process.setupKillAndExit();
perThreadSetup.setupRawDebug(_rawDebug);
perThreadSetup.setupHrtime(_hrtime, _hrtimeBigInt);
perThreadSetup.setupCpuUsage(_cpuUsage);
perThreadSetup.setupMemoryUsage(_memoryUsage);
perThreadSetup.setupKillAndExit();
if (global.__coverage__)
NativeModule.require('internal/process/write-coverage').setup();
@ -90,10 +114,9 @@
NativeModule.require('internal/inspector_async_hook').setup();
}
if (isMainThread)
_process.setupChannel();
_process.setupRawDebug(_rawDebug);
if (isMainThread) {
mainThreadSetup.setupChildProcessIpcChannel();
}
const browserGlobals = !process._noBrowserGlobals;
if (browserGlobals) {

View File

@ -1,24 +1,35 @@
'use strict';
// This file contains process bootstrappers that can only be
// run in the main thread
const {
ERR_INVALID_ARG_TYPE,
ERR_UNKNOWN_CREDENTIAL
} = require('internal/errors').codes;
errnoException,
codes: {
ERR_INVALID_ARG_TYPE,
ERR_UNKNOWN_CREDENTIAL
}
} = require('internal/errors');
const {
validateMode,
validateUint32
} = 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,
_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) {
setupPosixMethods(_initgroups, _setegid, _seteuid,
_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';
// This files contains process bootstrappers that can be
// run when setting up each thread, including the main
// thread and the worker threads.
const {
errnoException,
codes: {
@ -14,19 +18,19 @@ const {
} = require('internal/errors');
const util = require('util');
const constants = process.binding('constants').os.signals;
const assert = require('assert').strict;
const { deprecate } = require('internal/util');
const { isMainThread } = require('internal/worker');
process.assert = deprecate(
function(x, msg) {
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
},
'process.assert() is deprecated. Please use the `assert` module instead.',
'DEP0100');
function setupAssert() {
process.assert = deprecate(
function(x, msg) {
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
},
'process.assert() is deprecated. Please use the `assert` module instead.',
'DEP0100');
}
// 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.
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 upper/lower 32 bits of the second part of the value,
// and the remaining nanoseconds of the value.
function setup_hrtime(_hrtime, _hrtimeBigInt) {
function setupHrtime(_hrtime, _hrtimeBigInt) {
const hrValues = new Uint32Array(3);
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) {
process._rawDebug = function() {
_rawDebug(util.format.apply(null, arguments));
@ -288,13 +231,12 @@ function setupUncaughtExceptionCapture(exceptionHandlerState,
}
module.exports = {
setup_cpuUsage,
setup_hrtime,
setupAssert,
setupCpuUsage,
setupHrtime,
setupMemoryUsage,
setupConfig,
setupKillAndExit,
setupSignalHandlers,
setupChannel,
setupRawDebug,
setupUncaughtExceptionCapture
};

View File

@ -6,21 +6,17 @@ const {
ERR_UNKNOWN_STDIN_TYPE,
ERR_UNKNOWN_STREAM_TYPE
} = 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 stdout;
var stderr;
function getStdout() {
if (stdout) return stdout;
if (!isMainThread) return workerStdio.stdout;
stdout = createWritableStdioStream(1);
stdout.destroySoon = stdout.destroy;
stdout._destroy = function(er, cb) {
@ -36,7 +32,6 @@ function setupStdio() {
function getStderr() {
if (stderr) return stderr;
if (!isMainThread) return workerStdio.stderr;
stderr = createWritableStdioStream(2);
stderr.destroySoon = stderr.destroy;
stderr._destroy = function(er, cb) {
@ -52,8 +47,6 @@ function setupStdio() {
function getStdin() {
if (stdin) return stdin;
if (!isMainThread) return workerStdio.stdin;
const tty_wrap = process.binding('tty_wrap');
const fd = 0;
@ -136,6 +129,14 @@ function setupStdio() {
return stdin;
}
return {
getStdout,
getStderr,
getStdin
};
}
function setupProcessStdio({ getStdout, getStdin, getStderr }) {
Object.defineProperty(process, 'stdout', {
configurable: 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/priority_queue.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/per_thread.js',
'lib/internal/process/promises.js',
'lib/internal/process/stdio.js',
'lib/internal/process/warning.js',
'lib/internal/process.js',
'lib/internal/process/worker_thread_only.js',
'lib/internal/querystring.js',
'lib/internal/process/write-coverage.js',
'lib/internal/readline.js',