Skip to content

Commit

Permalink
process: split bootstrappers by threads that can run them
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
joyeecheung committed Jun 20, 2018
1 parent 3ff1cb9 commit 3a4fc4d
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 120 deletions.
67 changes: 45 additions & 22 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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');
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);
Expand Down Expand Up @@ -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
};
92 changes: 17 additions & 75 deletions lib/internal/process.js → lib/internal/process/per_thread.js
Original file line number Diff line number Diff line change
@@ -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: {
Expand All @@ -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);

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -288,13 +231,12 @@ function setupUncaughtExceptionCapture(exceptionHandlerState,
}

module.exports = {
setup_cpuUsage,
setup_hrtime,
setupAssert,
setupCpuUsage,
setupHrtime,
setupMemoryUsage,
setupConfig,
setupKillAndExit,
setupSignalHandlers,
setupChannel,
setupRawDebug,
setupUncaughtExceptionCapture
};
21 changes: 11 additions & 10 deletions lib/internal/process/stdio.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Expand All @@ -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;

Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 3a4fc4d

Please sign in to comment.