diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js index dd5ade20c3b044..399676b013f353 100644 --- a/lib/internal/bootstrap/node.js +++ b/lib/internal/bootstrap/node.js @@ -184,6 +184,7 @@ const rawMethods = internalBinding('process_methods'); process.hrtime = perThreadSetup.hrtime; process.hrtime.bigint = perThreadSetup.hrtimeBigInt; + process.constrainedMemory = perThreadSetup.constrainedMemory; process.openStdin = function() { process.stdin.resume(); diff --git a/lib/internal/process/per_thread.js b/lib/internal/process/per_thread.js index 6161a7be9f0fa9..9825f2730bda2a 100644 --- a/lib/internal/process/per_thread.js +++ b/lib/internal/process/per_thread.js @@ -60,6 +60,7 @@ const binding = internalBinding('process_methods'); let hrValues; let hrBigintValues; +let constrainedMemoryValus; function refreshHrtimeBuffer() { // The 3 entries filled in by the original process.hrtime contains @@ -69,6 +70,7 @@ function refreshHrtimeBuffer() { // Use a BigUint64Array in the closure because this is actually a bit // faster than simply returning a BigInt from C++ in V8 7.1. hrBigintValues = new BigUint64Array(binding.hrtimeBuffer, 0, 1); + constrainedMemoryValus = new BigUint64Array(binding.hrtimeBuffer, 0, 1); } // Create the buffers. @@ -100,6 +102,11 @@ function hrtimeBigInt() { return hrBigintValues[0]; } +function constrainedMemory() { + binding.constrainedMemory(); + return constrainedMemoryValus[0]; +} + function nop() {} // The execution of this function itself should not cause any side effects. @@ -427,4 +434,5 @@ module.exports = { hrtime, hrtimeBigInt, refreshHrtimeBuffer, + constrainedMemory, }; diff --git a/src/node_process.h b/src/node_process.h index 8065378960887d..96f9901976050b 100644 --- a/src/node_process.h +++ b/src/node_process.h @@ -81,6 +81,11 @@ class BindingData : public SnapshotableObject { static void SlowBigInt(const v8::FunctionCallbackInfo& args); + static void ConstrainedMemoryImpl(BindingData* receiver); + static void SlowGetConstrainedMemory( + const v8::FunctionCallbackInfo& args); + static void FastGetConstrainedMemory(v8::Local receiver); + private: static constexpr size_t kBufferSize = std::max(sizeof(uint64_t), sizeof(uint32_t) * 3); @@ -92,6 +97,7 @@ class BindingData : public SnapshotableObject { // time. static v8::CFunction fast_number_; static v8::CFunction fast_bigint_; + static v8::CFunction fast_get_constrained_memory_; }; } // namespace process diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index c429f1b50ee09a..44c06d9de8c2e1 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -471,11 +471,17 @@ BindingData::BindingData(Environment* env, v8::Local object) v8::CFunction BindingData::fast_number_(v8::CFunction::Make(FastNumber)); v8::CFunction BindingData::fast_bigint_(v8::CFunction::Make(FastBigInt)); +v8::CFunction BindingData::fast_get_constrained_memory_ = + v8::CFunction::Make(FastGetConstrainedMemory); void BindingData::AddMethods() { Local ctx = env()->context(); SetFastMethod(ctx, object(), "hrtime", SlowNumber, &fast_number_); SetFastMethod(ctx, object(), "hrtimeBigInt", SlowBigInt, &fast_bigint_); + SetFastMethod(ctx, object(), + "constrainedMemory", + SlowGetConstrainedMemory, + &fast_get_constrained_memory_); } void BindingData::RegisterExternalReferences( @@ -486,6 +492,9 @@ void BindingData::RegisterExternalReferences( registry->Register(FastBigInt); registry->Register(fast_number_.GetTypeInfo()); registry->Register(fast_bigint_.GetTypeInfo()); + registry->Register(SlowGetConstrainedMemory); + registry->Register(FastGetConstrainedMemory); + registry->Register(fast_get_constrained_memory_.GetTypeInfo()); } BindingData* BindingData::FromV8Value(Local value) { @@ -533,6 +542,23 @@ void BindingData::SlowNumber(const v8::FunctionCallbackInfo& args) { NumberImpl(FromJSObject(args.Holder())); } +void BindingData::ConstrainedMemoryImpl(BindingData* receiver) { + // Make sure we don't accidentally access buffers wiped for snapshot. + CHECK(!receiver->array_buffer_.IsEmpty()); + uint64_t t = uv_get_constrained_memory(); + uint64_t* fields = static_cast(receiver->backing_store_->Data()); + fields[0] = t; +} + +void BindingData::SlowGetConstrainedMemory( + const FunctionCallbackInfo& args) { + ConstrainedMemoryImpl(FromJSObject(args.Holder())); +} + +void BindingData::FastGetConstrainedMemory(v8::Local receiver) { + ConstrainedMemoryImpl(FromV8Value(receiver)); +} + bool BindingData::PrepareForSerialization(Local context, v8::SnapshotCreator* creator) { // It's not worth keeping. diff --git a/test/sequential/test-process-constrained-memory.js b/test/sequential/test-process-constrained-memory.js new file mode 100644 index 00000000000000..5a311b19b7e71a --- /dev/null +++ b/test/sequential/test-process-constrained-memory.js @@ -0,0 +1,12 @@ +'use strict'; +require('../common'); +const assert = require('assert'); +const { Worker } = require('worker_threads'); + +if (!process.env.isWorker) { + process.env.isWorker = true; + new Worker(__filename); + assert(process.constrainedMemory() >= 0); +} else { + assert(process.constrainedMemory() >= 0); +}