Implement multi-threading support for most of the API. Thanks to Stephen Belanger for reviewing this change in its original form, to Olivia Hugger for reviewing the documentation and some of the tests coming along with it, and to Alexey Orlenko and Timothy Gu for reviewing other parts of the tests. Refs: https://github.com/ayojs/ayo/pull/110 Refs: https://github.com/ayojs/ayo/pull/114 Refs: https://github.com/ayojs/ayo/pull/117 PR-URL: https://github.com/nodejs/node/pull/20876 Reviewed-By: Gireesh Punathil <gpunathi@in.ibm.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Shingo Inoue <leko.noor@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com> Reviewed-By: John-David Dalton <john.david.dalton@gmail.com> Reviewed-By: Gus Caplan <me@gus.host>
293 lines
7.8 KiB
JavaScript
293 lines
7.8 KiB
JavaScript
'use strict';
|
|
|
|
const {
|
|
errnoException,
|
|
codes: {
|
|
ERR_ASSERTION,
|
|
ERR_CPU_USAGE,
|
|
ERR_INVALID_ARG_TYPE,
|
|
ERR_INVALID_OPT_VALUE,
|
|
ERR_OUT_OF_RANGE,
|
|
ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET,
|
|
ERR_UNKNOWN_SIGNAL
|
|
}
|
|
} = 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');
|
|
|
|
// Set up the process.cpuUsage() function.
|
|
function setup_cpuUsage(_cpuUsage) {
|
|
// Create the argument array that will be passed to the native function.
|
|
const cpuValues = new Float64Array(2);
|
|
|
|
// Replace the native function with the JS version that calls the native
|
|
// function.
|
|
process.cpuUsage = function cpuUsage(prevValue) {
|
|
// If a previous value was passed in, ensure it has the correct shape.
|
|
if (prevValue) {
|
|
if (!previousValueIsValid(prevValue.user)) {
|
|
if (typeof prevValue !== 'object')
|
|
throw new ERR_INVALID_ARG_TYPE('prevValue', 'object', prevValue);
|
|
|
|
if (typeof prevValue.user !== 'number') {
|
|
throw new ERR_INVALID_ARG_TYPE('prevValue.user',
|
|
'number', prevValue.user);
|
|
}
|
|
throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.user',
|
|
prevValue.user);
|
|
}
|
|
|
|
if (!previousValueIsValid(prevValue.system)) {
|
|
if (typeof prevValue.system !== 'number') {
|
|
throw new ERR_INVALID_ARG_TYPE('prevValue.system',
|
|
'number', prevValue.system);
|
|
}
|
|
throw new ERR_INVALID_OPT_VALUE.RangeError('prevValue.system',
|
|
prevValue.system);
|
|
}
|
|
}
|
|
|
|
// Call the native function to get the current values.
|
|
const errmsg = _cpuUsage(cpuValues);
|
|
if (errmsg) {
|
|
throw new ERR_CPU_USAGE(errmsg);
|
|
}
|
|
|
|
// If a previous value was passed in, return diff of current from previous.
|
|
if (prevValue) {
|
|
return {
|
|
user: cpuValues[0] - prevValue.user,
|
|
system: cpuValues[1] - prevValue.system
|
|
};
|
|
}
|
|
|
|
// If no previous value passed in, return current value.
|
|
return {
|
|
user: cpuValues[0],
|
|
system: cpuValues[1]
|
|
};
|
|
};
|
|
|
|
// Ensure that a previously passed in value is valid. Currently, the native
|
|
// implementation always returns numbers <= Number.MAX_SAFE_INTEGER.
|
|
function previousValueIsValid(num) {
|
|
return Number.isFinite(num) &&
|
|
num <= Number.MAX_SAFE_INTEGER &&
|
|
num >= 0;
|
|
}
|
|
}
|
|
|
|
// 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) {
|
|
const hrValues = new Uint32Array(3);
|
|
|
|
process.hrtime = function hrtime(time) {
|
|
_hrtime(hrValues);
|
|
|
|
if (time !== undefined) {
|
|
if (!Array.isArray(time)) {
|
|
throw new ERR_INVALID_ARG_TYPE('time', 'Array', time);
|
|
}
|
|
if (time.length !== 2) {
|
|
throw new ERR_OUT_OF_RANGE('time', 2, time.length);
|
|
}
|
|
|
|
const sec = (hrValues[0] * 0x100000000 + hrValues[1]) - time[0];
|
|
const nsec = hrValues[2] - time[1];
|
|
const needsBorrow = nsec < 0;
|
|
return [needsBorrow ? sec - 1 : sec, needsBorrow ? nsec + 1e9 : nsec];
|
|
}
|
|
|
|
return [
|
|
hrValues[0] * 0x100000000 + hrValues[1],
|
|
hrValues[2]
|
|
];
|
|
};
|
|
}
|
|
|
|
function setupMemoryUsage(_memoryUsage) {
|
|
const memValues = new Float64Array(4);
|
|
|
|
process.memoryUsage = function memoryUsage() {
|
|
_memoryUsage(memValues);
|
|
return {
|
|
rss: memValues[0],
|
|
heapTotal: memValues[1],
|
|
heapUsed: memValues[2],
|
|
external: memValues[3]
|
|
};
|
|
};
|
|
}
|
|
|
|
function setupConfig(_source) {
|
|
// NativeModule._source
|
|
// used for `process.config`, but not a real module
|
|
const config = _source.config;
|
|
delete _source.config;
|
|
|
|
process.config = JSON.parse(config, function(key, value) {
|
|
if (value === 'true') return true;
|
|
if (value === 'false') return false;
|
|
return value;
|
|
});
|
|
}
|
|
|
|
|
|
function setupKillAndExit() {
|
|
|
|
process.exit = function(code) {
|
|
if (code || code === 0)
|
|
process.exitCode = code;
|
|
|
|
if (!process._exiting) {
|
|
process._exiting = true;
|
|
process.emit('exit', process.exitCode || 0);
|
|
}
|
|
process.reallyExit(process.exitCode || 0);
|
|
};
|
|
|
|
process.kill = function(pid, sig) {
|
|
var err;
|
|
|
|
// eslint-disable-next-line eqeqeq
|
|
if (pid != (pid | 0)) {
|
|
throw new ERR_INVALID_ARG_TYPE('pid', 'number', pid);
|
|
}
|
|
|
|
// preserve null signal
|
|
if (sig === (sig | 0)) {
|
|
err = process._kill(pid, sig);
|
|
} else {
|
|
sig = sig || 'SIGTERM';
|
|
if (constants[sig]) {
|
|
err = process._kill(pid, constants[sig]);
|
|
} else {
|
|
throw new ERR_UNKNOWN_SIGNAL(sig);
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
throw errnoException(err, 'kill');
|
|
|
|
return true;
|
|
};
|
|
}
|
|
|
|
|
|
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));
|
|
};
|
|
}
|
|
|
|
|
|
function setupUncaughtExceptionCapture(exceptionHandlerState,
|
|
shouldAbortOnUncaughtToggle) {
|
|
// shouldAbortOnUncaughtToggle is a typed array for faster
|
|
// communication with JS.
|
|
|
|
process.setUncaughtExceptionCaptureCallback = function(fn) {
|
|
if (fn === null) {
|
|
exceptionHandlerState.captureFn = fn;
|
|
shouldAbortOnUncaughtToggle[0] = 1;
|
|
return;
|
|
}
|
|
if (typeof fn !== 'function') {
|
|
throw new ERR_INVALID_ARG_TYPE('fn', ['Function', 'null'], fn);
|
|
}
|
|
if (exceptionHandlerState.captureFn !== null) {
|
|
throw new ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET();
|
|
}
|
|
exceptionHandlerState.captureFn = fn;
|
|
shouldAbortOnUncaughtToggle[0] = 0;
|
|
};
|
|
|
|
process.hasUncaughtExceptionCaptureCallback = function() {
|
|
return exceptionHandlerState.captureFn !== null;
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
setup_cpuUsage,
|
|
setup_hrtime,
|
|
setupMemoryUsage,
|
|
setupConfig,
|
|
setupKillAndExit,
|
|
setupSignalHandlers,
|
|
setupChannel,
|
|
setupRawDebug,
|
|
setupUncaughtExceptionCapture
|
|
};
|