Skip to content

Commit c5d496f

Browse files
committed
src,lib: add constrainedMemory API for process
1 parent 5d50b84 commit c5d496f

File tree

7 files changed

+83
-3
lines changed

7 files changed

+83
-3
lines changed

doc/api/process.md

+20
Original file line numberDiff line numberDiff line change
@@ -1103,6 +1103,25 @@ and [Cluster][] documentation), the `process.connected` property will return
11031103
Once `process.connected` is `false`, it is no longer possible to send messages
11041104
over the IPC channel using `process.send()`.
11051105

1106+
## `process.constrainedMemory()`
1107+
1108+
<!-- YAML
1109+
added: REPLACEME
1110+
-->
1111+
1112+
* {number}
1113+
1114+
Gets the amount of memory available to the process (in bytes) based on
1115+
limits imposed by the OS. If there is no such constraint, or the constraint
1116+
is unknown, `undefined` is returned.
1117+
1118+
It is not unusual for this value to be less than or greater than `os.totalmem()`.
1119+
This function currently only returns a non-zero value on Linux, based on cgroups
1120+
if it is present, and on z/OS based on `RLIMIT_MEMLIMIT`.
1121+
1122+
See [`uv_get_constrained_memory`][uv_get_constrained_memory] for more
1123+
information.
1124+
11061125
## `process.cpuUsage([previousValue])`
11071126

11081127
<!-- YAML
@@ -3901,6 +3920,7 @@ cases:
39013920
[process_warning]: #event-warning
39023921
[report documentation]: report.md
39033922
[terminal raw mode]: tty.md#readstreamsetrawmodemode
3923+
[uv_get_constrained_memory]: https://docs.libuv.org/en/v1.x/misc.html#c.uv_get_constrained_memory
39043924
[uv_rusage_t]: https://docs.libuv.org/en/v1.x/misc.html#c.uv_rusage_t
39053925
[wikipedia_major_fault]: https://en.wikipedia.org/wiki/Page_fault#Major
39063926
[wikipedia_minor_fault]: https://en.wikipedia.org/wiki/Page_fault#Minor

lib/internal/bootstrap/node.js

+1
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ const rawMethods = internalBinding('process_methods');
184184

185185
process.hrtime = perThreadSetup.hrtime;
186186
process.hrtime.bigint = perThreadSetup.hrtimeBigInt;
187+
process.constrainedMemory = perThreadSetup.constrainedMemory;
187188

188189
process.openStdin = function() {
189190
process.stdin.resume();

lib/internal/process/per_thread.js

+14
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ const binding = internalBinding('process_methods');
6060

6161
let hrValues;
6262
let hrBigintValues;
63+
let constrainedMemoryValues;
6364

6465
function refreshHrtimeBuffer() {
6566
// The 3 entries filled in by the original process.hrtime contains
@@ -71,8 +72,12 @@ function refreshHrtimeBuffer() {
7172
hrBigintValues = new BigUint64Array(binding.hrtimeBuffer, 0, 1);
7273
}
7374

75+
function refreshConstrainedMemoryValuesBuffer() {
76+
constrainedMemoryValues = new BigUint64Array(binding.hrtimeBuffer, 0, 1);
77+
}
7478
// Create the buffers.
7579
refreshHrtimeBuffer();
80+
refreshConstrainedMemoryValuesBuffer();
7681

7782
function hrtime(time) {
7883
binding.hrtime();
@@ -100,6 +105,13 @@ function hrtimeBigInt() {
100105
return hrBigintValues[0];
101106
}
102107

108+
function constrainedMemory() {
109+
binding.constrainedMemory();
110+
if (constrainedMemoryValues[0] > 0) {
111+
return constrainedMemoryValues[0];
112+
}
113+
}
114+
103115
function nop() {}
104116

105117
// The execution of this function itself should not cause any side effects.
@@ -427,4 +439,6 @@ module.exports = {
427439
hrtime,
428440
hrtimeBigInt,
429441
refreshHrtimeBuffer,
442+
refreshConstrainedMemoryValuesBuffer,
443+
constrainedMemory,
430444
};

lib/internal/process/pre_execution.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ function refreshRuntimeOptions() {
131131
function patchProcessObject(expandArgv1) {
132132
const binding = internalBinding('process_methods');
133133
binding.patchProcessObject(process);
134-
135-
require('internal/process/per_thread').refreshHrtimeBuffer();
136-
134+
const perThread = require('internal/process/per_thread');
135+
perThread.refreshHrtimeBuffer();
136+
perThread.refreshConstrainedMemoryValuesBuffer();
137137
ObjectDefineProperty(process, 'argv0', {
138138
__proto__: null,
139139
enumerable: true,

src/node_process.h

+6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ class BindingData : public SnapshotableObject {
8181

8282
static void SlowBigInt(const v8::FunctionCallbackInfo<v8::Value>& args);
8383

84+
static void ConstrainedMemoryImpl(BindingData* receiver);
85+
static void SlowGetConstrainedMemory(
86+
const v8::FunctionCallbackInfo<v8::Value>& args);
87+
static void FastGetConstrainedMemory(v8::Local<v8::Value> receiver);
88+
8489
private:
8590
static constexpr size_t kBufferSize =
8691
std::max(sizeof(uint64_t), sizeof(uint32_t) * 3);
@@ -92,6 +97,7 @@ class BindingData : public SnapshotableObject {
9297
// time.
9398
static v8::CFunction fast_number_;
9499
static v8::CFunction fast_bigint_;
100+
static v8::CFunction fast_get_constrained_memory_;
95101
};
96102

97103
} // namespace process

src/node_process_methods.cc

+27
Original file line numberDiff line numberDiff line change
@@ -471,11 +471,18 @@ BindingData::BindingData(Environment* env, v8::Local<v8::Object> object)
471471

472472
v8::CFunction BindingData::fast_number_(v8::CFunction::Make(FastNumber));
473473
v8::CFunction BindingData::fast_bigint_(v8::CFunction::Make(FastBigInt));
474+
v8::CFunction BindingData::fast_get_constrained_memory_ =
475+
v8::CFunction::Make(FastGetConstrainedMemory);
474476

475477
void BindingData::AddMethods() {
476478
Local<Context> ctx = env()->context();
477479
SetFastMethod(ctx, object(), "hrtime", SlowNumber, &fast_number_);
478480
SetFastMethod(ctx, object(), "hrtimeBigInt", SlowBigInt, &fast_bigint_);
481+
SetFastMethod(ctx,
482+
object(),
483+
"constrainedMemory",
484+
SlowGetConstrainedMemory,
485+
&fast_get_constrained_memory_);
479486
}
480487

481488
void BindingData::RegisterExternalReferences(
@@ -486,6 +493,9 @@ void BindingData::RegisterExternalReferences(
486493
registry->Register(FastBigInt);
487494
registry->Register(fast_number_.GetTypeInfo());
488495
registry->Register(fast_bigint_.GetTypeInfo());
496+
registry->Register(SlowGetConstrainedMemory);
497+
registry->Register(FastGetConstrainedMemory);
498+
registry->Register(fast_get_constrained_memory_.GetTypeInfo());
489499
}
490500

491501
BindingData* BindingData::FromV8Value(Local<Value> value) {
@@ -533,6 +543,23 @@ void BindingData::SlowNumber(const v8::FunctionCallbackInfo<v8::Value>& args) {
533543
NumberImpl(FromJSObject<BindingData>(args.Holder()));
534544
}
535545

546+
void BindingData::ConstrainedMemoryImpl(BindingData* receiver) {
547+
// Make sure we don't accidentally access buffers wiped for snapshot.
548+
CHECK(!receiver->array_buffer_.IsEmpty());
549+
uint64_t t = uv_get_constrained_memory();
550+
uint64_t* fields = static_cast<uint64_t*>(receiver->backing_store_->Data());
551+
fields[0] = t;
552+
}
553+
554+
void BindingData::SlowGetConstrainedMemory(
555+
const FunctionCallbackInfo<Value>& args) {
556+
ConstrainedMemoryImpl(FromJSObject<BindingData>(args.Holder()));
557+
}
558+
559+
void BindingData::FastGetConstrainedMemory(v8::Local<v8::Value> receiver) {
560+
ConstrainedMemoryImpl(FromV8Value(receiver));
561+
}
562+
536563
bool BindingData::PrepareForSerialization(Local<Context> context,
537564
v8::SnapshotCreator* creator) {
538565
// It's not worth keeping.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
const { Worker } = require('worker_threads');
5+
const constrainedMemory = process.constrainedMemory();
6+
if (constrainedMemory) {
7+
assert(process.constrainedMemory() > 0);
8+
}
9+
if (!process.env.isWorker) {
10+
process.env.isWorker = true;
11+
new Worker(__filename);
12+
}

0 commit comments

Comments
 (0)