Skip to content

Commit fa990cd

Browse files
committed
util: add getActiveResources
This change picks up the changes from the referenced PR and moves the implementation of `getActiveResources` to `util` to return a summary of the resources. Refs: #21453
1 parent 36decec commit fa990cd

8 files changed

+105
-23
lines changed

lib/internal/timers.js

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,11 @@ const kRefed = Symbol('refed');
139139
// Create a single linked list instance only once at startup
140140
const immediateQueue = new ImmediateList();
141141

142+
// If an uncaught exception was thrown during execution of immediateQueue,
143+
// this queue will store all remaining Immediates that need to run upon
144+
// resolution of all error handling (if process is still alive).
145+
const outstandingQueue = new ImmediateList();
146+
142147
let nextExpiry = Infinity;
143148
let refCount = 0;
144149

@@ -279,11 +284,11 @@ ImmediateList.prototype.append = function(item) {
279284
// Removes an item from the linked list, adjusting the pointers of adjacent
280285
// items and the linked list's head or tail pointers as necessary
281286
ImmediateList.prototype.remove = function(item) {
282-
if (item._idleNext) {
287+
if (item._idleNext !== null) {
283288
item._idleNext._idlePrev = item._idlePrev;
284289
}
285290

286-
if (item._idlePrev) {
291+
if (item._idlePrev !== null) {
287292
item._idlePrev._idleNext = item._idleNext;
288293
}
289294

@@ -413,11 +418,6 @@ function setPosition(node, pos) {
413418
}
414419

415420
function getTimerCallbacks(runNextTicks) {
416-
// If an uncaught exception was thrown during execution of immediateQueue,
417-
// this queue will store all remaining Immediates that need to run upon
418-
// resolution of all error handling (if process is still alive).
419-
const outstandingQueue = new ImmediateList();
420-
421421
function processImmediate() {
422422
const queue = outstandingQueue.head !== null ?
423423
outstandingQueue : immediateQueue;
@@ -649,6 +649,7 @@ module.exports = {
649649
setUnrefTimeout,
650650
getTimerDuration,
651651
immediateQueue,
652+
outstandingQueue,
652653
getTimerCallbacks,
653654
immediateInfoFields: {
654655
kCount,

lib/internal/util.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
const {
44
ArrayFrom,
55
ArrayIsArray,
6+
ArrayPrototypeForEach,
67
ArrayPrototypePush,
78
ArrayPrototypeSlice,
89
ArrayPrototypeSort,
@@ -14,6 +15,7 @@ const {
1415
ObjectGetOwnPropertyDescriptors,
1516
ObjectGetPrototypeOf,
1617
ObjectSetPrototypeOf,
18+
ObjectValues,
1719
Promise,
1820
ReflectApply,
1921
ReflectConstruct,
@@ -45,6 +47,10 @@ const {
4547
sleep: _sleep
4648
} = internalBinding('util');
4749
const { isNativeError } = internalBinding('types');
50+
const {
51+
_getActiveHandles,
52+
_getActiveRequests,
53+
} = internalBinding('process_methods');
4854

4955
const noCrypto = !process.versions.openssl;
5056

@@ -441,6 +447,39 @@ function createDeferredPromise() {
441447
return { promise, resolve, reject };
442448
}
443449

450+
let internalTimers;
451+
452+
function getActiveResources() {
453+
if (internalTimers == null) {
454+
internalTimers = require('internal/timers');
455+
}
456+
const handles = _getActiveHandles();
457+
const reqs = _getActiveRequests();
458+
459+
const timers = {};
460+
ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => {
461+
let timer = list._idlePrev === list ? null : list._idlePrev;
462+
463+
while (timer !== null) {
464+
timers[timer[internalTimers.async_id_symbol]] = timer;
465+
466+
timer = timer._idlePrev === list ? null : list._idlePrev;
467+
}
468+
});
469+
470+
const immediates = {};
471+
const queue = internalTimers.outstandingQueue.head !== null ?
472+
internalTimers.outstandingQueue : internalTimers.immediateQueue;
473+
let immediate = queue.head;
474+
while (immediate !== null) {
475+
immediates[immediate[internalTimers.async_id_symbol]] = immediate;
476+
477+
immediate = immediate._idleNext;
478+
}
479+
480+
return { ...handles, ...reqs, ...timers, ...immediates };
481+
}
482+
444483
module.exports = {
445484
assertCrypto,
446485
cachedResult,
@@ -451,6 +490,7 @@ module.exports = {
451490
deprecate,
452491
emitExperimentalWarning,
453492
filterDuplicateStrings,
493+
getActiveResources,
454494
getConstructorOf,
455495
getSystemErrorMap,
456496
getSystemErrorName,

lib/util.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ const types = require('internal/util/types');
6767

6868
const {
6969
deprecate,
70+
getActiveResources,
7071
getSystemErrorMap,
7172
getSystemErrorName: internalErrorName,
7273
promisify
@@ -257,6 +258,7 @@ module.exports = {
257258
deprecate,
258259
format,
259260
formatWithOptions,
261+
getActiveResources,
260262
getSystemErrorMap,
261263
getSystemErrorName,
262264
inherits,

src/node_process_methods.cc

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include "async_wrap-inl.h"
12
#include "base_object-inl.h"
23
#include "debug_utils-inl.h"
34
#include "env-inl.h"
@@ -35,7 +36,6 @@ typedef int mode_t;
3536
namespace node {
3637

3738
using v8::ApiObject;
38-
using v8::Array;
3939
using v8::ArrayBuffer;
4040
using v8::BackingStore;
4141
using v8::CFunction;
@@ -253,31 +253,43 @@ static void Uptime(const FunctionCallbackInfo<Value>& args) {
253253
static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
254254
Environment* env = Environment::GetCurrent(args);
255255

256-
std::vector<Local<Value>> request_v;
256+
Local<Context> ctx = env->context();
257+
Local<Object> return_obj = Object::New(args.GetIsolate());
258+
257259
for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) {
258260
AsyncWrap* w = req_wrap->GetAsyncWrap();
259261
if (w->persistent().IsEmpty())
260262
continue;
261-
request_v.emplace_back(w->GetOwner());
263+
double async_id = w->get_async_id();
264+
Local<Object> req_obj = w->object();
265+
266+
USE(return_obj->Set(ctx,
267+
Number::New(args.GetIsolate(), async_id),
268+
req_obj));
262269
}
263270

264-
args.GetReturnValue().Set(
265-
Array::New(env->isolate(), request_v.data(), request_v.size()));
271+
args.GetReturnValue().Set(return_obj);
266272
}
267273

268274
// Non-static, friend of HandleWrap. Could have been a HandleWrap method but
269275
// implemented here for consistency with GetActiveRequests().
270276
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
271277
Environment* env = Environment::GetCurrent(args);
272278

273-
std::vector<Local<Value>> handle_v;
279+
Local<Context> ctx = env->context();
280+
Local<Object> return_obj = Object::New(args.GetIsolate());
281+
274282
for (auto w : *env->handle_wrap_queue()) {
275-
if (!HandleWrap::HasRef(w))
283+
if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w))
276284
continue;
277-
handle_v.emplace_back(w->GetOwner());
285+
double async_id = w->get_async_id();
286+
Local<Object> handle_object = w->object();
287+
USE(return_obj->Set(ctx, Number::New(args.GetIsolate(),
288+
async_id),
289+
handle_object));
278290
}
279-
args.GetReturnValue().Set(
280-
Array::New(env->isolate(), handle_v.data(), handle_v.size()));
291+
292+
args.GetReturnValue().Set(return_obj);
281293
}
282294

283295
static void ResourceUsage(const FunctionCallbackInfo<Value>& args) {

test/parallel/test-handle-wrap-isrefed.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
const common = require('../common');
55
const strictEqual = require('assert').strictEqual;
66
const { internalBinding } = require('internal/test/binding');
7+
const { getActiveResources } = require('util');
78

89
// child_process
910
{
@@ -107,4 +108,25 @@ const { kStateSymbol } = require('internal/dgram');
107108
}
108109

109110

111+
// timers
112+
{
113+
const { Timeout } = require('internal/timers');
114+
strictEqual(Object.values(getActiveResources()).filter(
115+
(handle) => (handle instanceof Timeout)).length, 0);
116+
const timer = setTimeout(() => {}, 500);
117+
const handles = Object.values(getActiveResources()).filter(
118+
(handle) => (handle instanceof Timeout));
119+
strictEqual(handles.length, 1);
120+
const handle = handles[0];
121+
strictEqual(Object.getPrototypeOf(handle).hasOwnProperty('hasRef'),
122+
true, 'timer: hasRef() missing');
123+
strictEqual(handle.hasRef(), true);
124+
timer.unref();
125+
strictEqual(handle.hasRef(),
126+
false, 'timer: unref() ineffective');
127+
timer.ref();
128+
strictEqual(handle.hasRef(),
129+
true, 'timer: ref() ineffective');
130+
}
131+
110132
// See also test/pseudo-tty/test-handle-wrap-isrefed-tty.js

test/parallel/test-process-getactiverequests.js renamed to test/parallel/test-util-getactiveresources-requests.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
const common = require('../common');
44
const assert = require('assert');
55
const fs = require('fs');
6+
const { getActiveResources } = require('util');
67

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

10-
assert.strictEqual(process._getActiveRequests().length, 12);
11+
assert.strictEqual(Object.values(getActiveResources()).length, 12);

test/parallel/test-process-getactivehandles.js renamed to test/parallel/test-util-getactiveresources.js

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
require('../common');
44
const assert = require('assert');
55
const net = require('net');
6+
const { getActiveResources } = require('util');
7+
68
const NUM = 8;
79
const connections = [];
810
const clients = [];
@@ -30,18 +32,18 @@ function clientConnected(client) {
3032

3133

3234
function checkAll() {
33-
const handles = process._getActiveHandles();
35+
const handles = Object.values(getActiveResources());
3436

3537
clients.forEach(function(item) {
36-
assert.ok(handles.includes(item));
38+
assert.ok(handles.includes(item._handle));
3739
item.destroy();
3840
});
3941

4042
connections.forEach(function(item) {
41-
assert.ok(handles.includes(item));
43+
assert.ok(handles.includes(item._handle));
4244
item.end();
4345
});
4446

45-
assert.ok(handles.includes(server));
47+
assert.ok(handles.includes(server._handle));
4648
server.close();
4749
}

test/pseudo-tty/ref_keeps_node_running.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require('../common');
66
const { internalBinding } = require('internal/test/binding');
77
const { TTY, isTTY } = internalBinding('tty_wrap');
88
const strictEqual = require('assert').strictEqual;
9+
const { getActiveResources } = require('util');
910

1011
strictEqual(isTTY(0), true, 'fd 0 is not a TTY');
1112

@@ -14,7 +15,8 @@ handle.readStart();
1415
handle.onread = () => {};
1516

1617
function isHandleActive(handle) {
17-
return process._getActiveHandles().some((active) => active === handle);
18+
return Object.values(getActiveResources())
19+
.some((active) => active === handle);
1820
}
1921

2022
strictEqual(isHandleActive(handle), true, 'TTY handle not initially active');

0 commit comments

Comments
 (0)