Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process: add getActiveResourcesInfo() #40813

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions doc/api/process.md
Original file line number Diff line number Diff line change
@@ -1817,6 +1817,44 @@ a code.
Specifying a code to [`process.exit(code)`][`process.exit()`] will override any
previous setting of `process.exitCode`.

## `process.getActiveResourcesInfo()`

<!-- YAML
added: REPLACEME
-->

> Stability: 1 - Experimental

* Returns: {string\[]}

The `process.getActiveResourcesInfo()` method returns an array of strings
containing the types of the active resources that are currently keeping the
event loop alive.

```mjs
import { getActiveResourcesInfo } from 'process';
import { setTimeout } from 'timers';

console.log('Before:', getActiveResourcesInfo());
setTimeout(() => {}, 1000);
console.log('After:', getActiveResourcesInfo());
// Prints:
// Before: [ 'CloseReq', 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
// After: [ 'CloseReq', 'TTYWrap', 'TTYWrap', 'TTYWrap', 'Timeout' ]
```

```cjs
const { getActiveResourcesInfo } = require('process');
const { setTimeout } = require('timers');

console.log('Before:', getActiveResourcesInfo());
setTimeout(() => {}, 1000);
console.log('After:', getActiveResourcesInfo());
// Prints:
// Before: [ 'TTYWrap', 'TTYWrap', 'TTYWrap' ]
// After: [ 'TTYWrap', 'TTYWrap', 'TTYWrap', 'Timeout' ]
```

## `process.getegid()`

<!-- YAML
21 changes: 19 additions & 2 deletions lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
@@ -39,19 +39,24 @@
setupPrepareStackTrace();

const {
ArrayPrototypeConcat,
ArrayPrototypeFilter,
ArrayPrototypeMap,
FunctionPrototypeCall,
JSONParse,
ObjectDefineProperty,
ObjectDefineProperties,
ObjectGetPrototypeOf,
ObjectPreventExtensions,
ObjectSetPrototypeOf,
ObjectValues,
ReflectGet,
ReflectSet,
SymbolToStringTag,
globalThis,
} = primordials;
const config = internalBinding('config');
const internalTimers = require('internal/timers');
const { deprecate, lazyDOMExceptionClass } = require('internal/util');

setupProcessObject();
@@ -150,6 +155,16 @@ const rawMethods = internalBinding('process_methods');
process._getActiveRequests = rawMethods._getActiveRequests;
process._getActiveHandles = rawMethods._getActiveHandles;

process.getActiveResourcesInfo = function() {
return ArrayPrototypeConcat(
rawMethods._getActiveRequestsInfo(),
rawMethods._getActiveHandlesInfo(),
ArrayPrototypeMap(
ArrayPrototypeFilter(ObjectValues(internalTimers.activeTimersMap),
({ resource }) => resource.hasRef()),
({ type }) => type));
};

// TODO(joyeecheung): remove these
process.reallyExit = rawMethods.reallyExit;
process._kill = rawMethods._kill;
@@ -360,9 +375,11 @@ process.emitWarning = emitWarning;
// TODO(joyeecheung): either remove it or make it public
process._tickCallback = runNextTicks;

const { getTimerCallbacks } = require('internal/timers');
const { setupTimers } = internalBinding('timers');
const { processImmediate, processTimers } = getTimerCallbacks(runNextTicks);
const {
processImmediate,
processTimers,
} = internalTimers.getTimerCallbacks(runNextTicks);
// Sets two per-Environment callbacks that will be run from libuv:
// - processImmediate will be run in the callback of the per-Environment
// check handle.
13 changes: 13 additions & 0 deletions lib/internal/timers.js
Original file line number Diff line number Diff line change
@@ -139,6 +139,12 @@ const kRefed = Symbol('refed');
// Create a single linked list instance only once at startup
const immediateQueue = new ImmediateList();

// Object map containing timers
//
// - key = asyncId
// - value = { type, resource }
const activeTimersMap = ObjectCreate(null);

let nextExpiry = Infinity;
let refCount = 0;

@@ -160,6 +166,7 @@ function initAsyncResource(resource, type) {
resource[trigger_async_id_symbol] = getDefaultTriggerAsyncId();
if (initHooksExist())
emitInit(asyncId, type, triggerAsyncId, resource);
activeTimersMap[asyncId] = { type, resource };
}

// Timer constructor function.
@@ -446,6 +453,8 @@ function getTimerCallbacks(runNextTicks) {
continue;
}

// TODO(RaisinTen): Destroy and unref the Immediate after _onImmediate()
// gets executed, just like how Timeouts work.
immediate._destroyed = true;

immediateInfo[kCount]--;
@@ -469,6 +478,7 @@ function getTimerCallbacks(runNextTicks) {

if (destroyHooksExist())
emitDestroy(asyncId);
delete activeTimersMap[asyncId];

outstandingQueue.head = immediate = immediate._idleNext;
}
@@ -541,6 +551,7 @@ function getTimerCallbacks(runNextTicks) {

if (destroyHooksExist())
emitDestroy(asyncId);
delete activeTimersMap[asyncId];
}
continue;
}
@@ -569,6 +580,7 @@ function getTimerCallbacks(runNextTicks) {

if (destroyHooksExist())
emitDestroy(asyncId);
delete activeTimersMap[asyncId];
}
}

@@ -658,6 +670,7 @@ module.exports = {
active,
unrefActive,
insert,
activeTimersMap,
timerListMap,
timerListQueue,
decRefCount,
3 changes: 3 additions & 0 deletions lib/timers.js
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ const {
kRefed,
kHasPrimitive,
getTimerDuration,
activeTimersMap,
timerListMap,
timerListQueue,
immediateQueue,
@@ -87,6 +88,7 @@ function unenroll(item) {
// Fewer checks may be possible, but these cover everything.
if (destroyHooksExist() && item[async_id_symbol] !== undefined)
emitDestroy(item[async_id_symbol]);
delete activeTimersMap[item[async_id_symbol]];

L.remove(item);

@@ -329,6 +331,7 @@ function clearImmediate(immediate) {
if (destroyHooksExist() && immediate[async_id_symbol] !== undefined) {
emitDestroy(immediate[async_id_symbol]);
}
delete activeTimersMap[immediate[async_id_symbol]];

immediate._onImmediate = null;

34 changes: 34 additions & 0 deletions src/node_process_methods.cc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include "async_wrap-inl.h"
#include "base_object-inl.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
@@ -257,6 +258,21 @@ static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
Array::New(env->isolate(), request_v.data(), request_v.size()));
}

static void GetActiveRequestsInfo(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> requests_info;
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
AsyncWrap* w = req_wrap->GetAsyncWrap();
if (w->persistent().IsEmpty()) continue;
requests_info.emplace_back(OneByteString(env->isolate(),
w->MemoryInfoName().c_str()));
}

args.GetReturnValue().Set(
Array::New(env->isolate(), requests_info.data(), requests_info.size()));
}

// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
// implemented here for consistency with GetActiveRequests().
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
@@ -272,6 +288,20 @@ void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
}

void GetActiveHandlesInfo(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> handles_info;
for (HandleWrap* w : *env->handle_wrap_queue()) {
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue;
handles_info.emplace_back(OneByteString(env->isolate(),
w->MemoryInfoName().c_str()));
}

args.GetReturnValue().Set(
Array::New(env->isolate(), handles_info.data(), handles_info.size()));
}

static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

@@ -547,7 +577,9 @@ static void Initialize(Local<Object> target,
env->SetMethod(target, "resourceUsage", ResourceUsage);

env->SetMethod(target, "_getActiveRequests", GetActiveRequests);
env->SetMethod(target, "_getActiveRequestsInfo", GetActiveRequestsInfo);
env->SetMethod(target, "_getActiveHandles", GetActiveHandles);
env->SetMethod(target, "_getActiveHandlesInfo", GetActiveHandlesInfo);
env->SetMethod(target, "_kill", Kill);

env->SetMethodNoSideEffect(target, "cwd", Cwd);
@@ -574,7 +606,9 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
registry->Register(ResourceUsage);

registry->Register(GetActiveRequests);
registry->Register(GetActiveRequestsInfo);
registry->Register(GetActiveHandles);
registry->Register(GetActiveHandlesInfo);
registry->Register(Kill);

registry->Register(Cwd);
26 changes: 26 additions & 0 deletions test/parallel/test-handle-wrap-isrefed.js
Original file line number Diff line number Diff line change
@@ -106,5 +106,31 @@ const { kStateSymbol } = require('internal/dgram');
false, 'tcp_wrap: not unrefed on close')));
}

// timers
{
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 0);
const timeout = setTimeout(() => {}, 500);
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 1);
timeout.unref();
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 0);
timeout.ref();
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 1);

strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Immediate').length, 0);
const immediate = setImmediate(() => {});
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Immediate').length, 1);
immediate.unref();
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Immediate').length, 0);
immediate.ref();
strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Immediate').length, 1);
}

// See also test/pseudo-tty/test-handle-wrap-isrefed-tty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
'use strict';

require('../common');
const assert = require('assert');
const net = require('net');
const NUM = 8;
const connections = [];
const clients = [];
let clients_counter = 0;

const server = net.createServer(function listener(c) {
connections.push(c);
}).listen(0, makeConnection);


function makeConnection() {
if (clients_counter >= NUM) return;
net.connect(server.address().port, function connected() {
clientConnected(this);
makeConnection();
});
}


function clientConnected(client) {
clients.push(client);
if (++clients_counter >= NUM)
checkAll();
}


function checkAll() {
assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'TCPSocketWrap').length,
clients.length + connections.length);

clients.forEach((item) => item.destroy());
connections.forEach((item) => item.end());

assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'TCPServerWrap').length, 1);

server.close();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
'use strict';

const common = require('../common');
const assert = require('assert');
const fs = require('fs');

for (let i = 0; i < 12; i++) {
fs.open(__filename, 'r', common.mustCall());
}

assert.strictEqual(process.getActiveResourcesInfo().length, 12);
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const common = require('../common');

const assert = require('assert');

assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 0);

let count = 0;
const interval = setInterval(common.mustCall(() => {
assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 1);
++count;
if (count === 3) {
clearInterval(interval);
}
}, 3), 0);

assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 1);
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
'use strict';

const common = require('../common');
const assert = require('assert');

for (let i = 0; i < 10; ++i) {
for (let j = 0; j < 10; ++j) {
setTimeout(common.mustCall(), i);
}
}

assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Timeout').length, 100);

for (let i = 0; i < 10; ++i) {
setImmediate(common.mustCall());
}

assert.strictEqual(process.getActiveResourcesInfo().filter(
(type) => type === 'Immediate').length, 10);
Loading