Skip to content

Commit

Permalink
bootstrap: build fast APIs in pre-execution
Browse files Browse the repository at this point in the history
Fast APIs need to work with ArrayBuffers which we need
to rebuild connections to after deserializing them
from the snapshot. For now, postpone their creation
until pre-execution to simplify the process.

PR-URL: #32984
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
  • Loading branch information
joyeecheung committed Jul 18, 2020
1 parent c943cb4 commit f8bde7c
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 51 deletions.
2 changes: 0 additions & 2 deletions lib/internal/bootstrap/node.js
Expand Up @@ -80,8 +80,6 @@ const rawMethods = internalBinding('process_methods');

const wrapped = perThreadSetup.wrapProcessMethods(rawMethods);
process._rawDebug = wrapped._rawDebug;
process.hrtime = wrapped.hrtime;
process.hrtime.bigint = wrapped.hrtimeBigInt;
process.cpuUsage = wrapped.cpuUsage;
process.resourceUsage = wrapped.resourceUsage;
process.memoryUsage = wrapped.memoryUsage;
Expand Down
14 changes: 11 additions & 3 deletions lib/internal/bootstrap/pre_execution.js
Expand Up @@ -74,11 +74,19 @@ function prepareMainThreadExecution(expandArgv1 = false) {
}

function patchProcessObject(expandArgv1) {
const binding = internalBinding('process_methods');
binding.patchProcessObject(process);

// TODO(joyeecheung): snapshot fast APIs (which need to work with
// array buffers, whose connection with C++ needs to be rebuilt after
// deserialization).
const {
patchProcessObject: patchProcessObjectNative
} = internalBinding('process_methods');
hrtime,
hrtimeBigInt
} = require('internal/process/per_thread').getFastAPIs(binding);

patchProcessObjectNative(process);
process.hrtime = hrtime;
process.hrtime.bigint = hrtimeBigInt;

ObjectDefineProperty(process, 'argv0', {
enumerable: true,
Expand Down
87 changes: 48 additions & 39 deletions lib/internal/process/per_thread.js
Expand Up @@ -37,10 +37,56 @@ function assert(x, msg) {
if (!x) throw new ERR_ASSERTION(msg || 'assertion error');
}

function getFastAPIs(binding) {
const {
hrtime: _hrtime
} = binding.getFastAPIs();

// 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.
const hrValues = new Uint32Array(_hrtime.buffer);

function hrtime(time) {
_hrtime.hrtime();

if (time !== undefined) {
if (!ArrayIsArray(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]
];
}

// Use a BigUint64Array in the closure because this is actually a bit
// faster than simply returning a BigInt from C++ in V8 7.1.
const hrBigintValues = new BigUint64Array(_hrtime.buffer, 0, 1);
function hrtimeBigInt() {
_hrtime.hrtimeBigInt();
return hrBigintValues[0];
}

return {
hrtime,
hrtimeBigInt,
};
}

// The execution of this function itself should not cause any side effects.
function wrapProcessMethods(binding) {
const {
hrtime: _hrtime,
cpuUsage: _cpuUsage,
memoryUsage: _memoryUsage,
resourceUsage: _resourceUsage
Expand Down Expand Up @@ -109,42 +155,6 @@ function wrapProcessMethods(binding) {
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.
const hrValues = new Uint32Array(_hrtime.buffer);

function hrtime(time) {
_hrtime.hrtime();

if (time !== undefined) {
if (!ArrayIsArray(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]
];
}

// Use a BigUint64Array in the closure because this is actually a bit
// faster than simply returning a BigInt from C++ in V8 7.1.
const hrBigintValues = new BigUint64Array(_hrtime.buffer, 0, 1);
function hrtimeBigInt() {
_hrtime.hrtimeBigInt();
return hrBigintValues[0];
}

const memValues = new Float64Array(5);
function memoryUsage() {
_memoryUsage(memValues);
Expand Down Expand Up @@ -225,8 +235,6 @@ function wrapProcessMethods(binding) {

return {
_rawDebug,
hrtime,
hrtimeBigInt,
cpuUsage,
resourceUsage,
memoryUsage,
Expand Down Expand Up @@ -356,6 +364,7 @@ function toggleTraceCategoryState(asyncHooksEnabled) {

module.exports = {
toggleTraceCategoryState,
getFastAPIs,
assert,
buildAllowedFlags,
wrapProcessMethods
Expand Down
17 changes: 11 additions & 6 deletions src/node_process_methods.cc
Expand Up @@ -504,6 +504,16 @@ class FastHrtime : public BaseObject {
std::shared_ptr<BackingStore> backing_store_;
};

static void GetFastAPIs(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Object> ret = Object::New(env->isolate());
ret->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"),
FastHrtime::New(env))
.ToChecked();
args.GetReturnValue().Set(ret);
}

static void InitializeProcessMethods(Local<Object> target,
Local<Value> unused,
Local<Context> context,
Expand Down Expand Up @@ -534,12 +544,7 @@ static void InitializeProcessMethods(Local<Object> target,
env->SetMethod(target, "reallyExit", ReallyExit);
env->SetMethodNoSideEffect(target, "uptime", Uptime);
env->SetMethod(target, "patchProcessObject", PatchProcessObject);

target
->Set(env->context(),
FIXED_ONE_BYTE_STRING(env->isolate(), "hrtime"),
FastHrtime::New(env))
.ToChecked();
env->SetMethod(target, "getFastAPIs", GetFastAPIs);
}

} // namespace node
Expand Down
2 changes: 1 addition & 1 deletion test/cctest/test_base_object_ptr.cc
Expand Up @@ -16,7 +16,7 @@ using v8::Object;

// Environments may come with existing BaseObject instances.
// This variable offsets the expected BaseObject counts.
static const int BASE_OBJECT_COUNT = 1;
static const int BASE_OBJECT_COUNT = 0;

class BaseObjectPtrTest : public EnvironmentTestFixture {};

Expand Down

0 comments on commit f8bde7c

Please sign in to comment.