From fa990cd0b40cad40f7bb912def21055e605bf075 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Sun, 7 Feb 2021 14:59:43 +0530 Subject: [PATCH 01/11] 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: https://github.com/nodejs/node/pull/21453 --- lib/internal/timers.js | 15 +++---- lib/internal/util.js | 40 +++++++++++++++++++ lib/util.js | 2 + src/node_process_methods.cc | 32 ++++++++++----- test/parallel/test-handle-wrap-isrefed.js | 22 ++++++++++ ... test-util-getactiveresources-requests.js} | 3 +- ...les.js => test-util-getactiveresources.js} | 10 +++-- test/pseudo-tty/ref_keeps_node_running.js | 4 +- 8 files changed, 105 insertions(+), 23 deletions(-) rename test/parallel/{test-process-getactiverequests.js => test-util-getactiveresources-requests.js} (62%) rename test/parallel/{test-process-getactivehandles.js => test-util-getactiveresources.js} (75%) diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 9a07fe3348f65e..4e6caa18de5f05 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -139,6 +139,11 @@ const kRefed = Symbol('refed'); // Create a single linked list instance only once at startup const immediateQueue = new ImmediateList(); +// If an uncaught exception was thrown during execution of immediateQueue, +// this queue will store all remaining Immediates that need to run upon +// resolution of all error handling (if process is still alive). +const outstandingQueue = new ImmediateList(); + let nextExpiry = Infinity; let refCount = 0; @@ -279,11 +284,11 @@ ImmediateList.prototype.append = function(item) { // Removes an item from the linked list, adjusting the pointers of adjacent // items and the linked list's head or tail pointers as necessary ImmediateList.prototype.remove = function(item) { - if (item._idleNext) { + if (item._idleNext !== null) { item._idleNext._idlePrev = item._idlePrev; } - if (item._idlePrev) { + if (item._idlePrev !== null) { item._idlePrev._idleNext = item._idleNext; } @@ -413,11 +418,6 @@ function setPosition(node, pos) { } function getTimerCallbacks(runNextTicks) { - // If an uncaught exception was thrown during execution of immediateQueue, - // this queue will store all remaining Immediates that need to run upon - // resolution of all error handling (if process is still alive). - const outstandingQueue = new ImmediateList(); - function processImmediate() { const queue = outstandingQueue.head !== null ? outstandingQueue : immediateQueue; @@ -649,6 +649,7 @@ module.exports = { setUnrefTimeout, getTimerDuration, immediateQueue, + outstandingQueue, getTimerCallbacks, immediateInfoFields: { kCount, diff --git a/lib/internal/util.js b/lib/internal/util.js index f77f1fc43368c4..69bdd740327022 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -3,6 +3,7 @@ const { ArrayFrom, ArrayIsArray, + ArrayPrototypeForEach, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSort, @@ -14,6 +15,7 @@ const { ObjectGetOwnPropertyDescriptors, ObjectGetPrototypeOf, ObjectSetPrototypeOf, + ObjectValues, Promise, ReflectApply, ReflectConstruct, @@ -45,6 +47,10 @@ const { sleep: _sleep } = internalBinding('util'); const { isNativeError } = internalBinding('types'); +const { + _getActiveHandles, + _getActiveRequests, +} = internalBinding('process_methods'); const noCrypto = !process.versions.openssl; @@ -441,6 +447,39 @@ function createDeferredPromise() { return { promise, resolve, reject }; } +let internalTimers; + +function getActiveResources() { + if (internalTimers == null) { + internalTimers = require('internal/timers'); + } + const handles = _getActiveHandles(); + const reqs = _getActiveRequests(); + + const timers = {}; + ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => { + let timer = list._idlePrev === list ? null : list._idlePrev; + + while (timer !== null) { + timers[timer[internalTimers.async_id_symbol]] = timer; + + timer = timer._idlePrev === list ? null : list._idlePrev; + } + }); + + const immediates = {}; + const queue = internalTimers.outstandingQueue.head !== null ? + internalTimers.outstandingQueue : internalTimers.immediateQueue; + let immediate = queue.head; + while (immediate !== null) { + immediates[immediate[internalTimers.async_id_symbol]] = immediate; + + immediate = immediate._idleNext; + } + + return { ...handles, ...reqs, ...timers, ...immediates }; +} + module.exports = { assertCrypto, cachedResult, @@ -451,6 +490,7 @@ module.exports = { deprecate, emitExperimentalWarning, filterDuplicateStrings, + getActiveResources, getConstructorOf, getSystemErrorMap, getSystemErrorName, diff --git a/lib/util.js b/lib/util.js index f3c10bb7720e58..0bce55d315ad98 100644 --- a/lib/util.js +++ b/lib/util.js @@ -67,6 +67,7 @@ const types = require('internal/util/types'); const { deprecate, + getActiveResources, getSystemErrorMap, getSystemErrorName: internalErrorName, promisify @@ -257,6 +258,7 @@ module.exports = { deprecate, format, formatWithOptions, + getActiveResources, getSystemErrorMap, getSystemErrorName, inherits, diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index 5030ab872f4f16..c3911205036c16 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -1,3 +1,4 @@ +#include "async_wrap-inl.h" #include "base_object-inl.h" #include "debug_utils-inl.h" #include "env-inl.h" @@ -35,7 +36,6 @@ typedef int mode_t; namespace node { using v8::ApiObject; -using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; using v8::CFunction; @@ -253,16 +253,22 @@ static void Uptime(const FunctionCallbackInfo& args) { static void GetActiveRequests(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - std::vector> request_v; + Local ctx = env->context(); + Local return_obj = Object::New(args.GetIsolate()); + for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) { AsyncWrap* w = req_wrap->GetAsyncWrap(); if (w->persistent().IsEmpty()) continue; - request_v.emplace_back(w->GetOwner()); + double async_id = w->get_async_id(); + Local req_obj = w->object(); + + USE(return_obj->Set(ctx, + Number::New(args.GetIsolate(), async_id), + req_obj)); } - args.GetReturnValue().Set( - Array::New(env->isolate(), request_v.data(), request_v.size())); + args.GetReturnValue().Set(return_obj); } // Non-static, friend of HandleWrap. Could have been a HandleWrap method but @@ -270,14 +276,20 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { void GetActiveHandles(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); - std::vector> handle_v; + Local ctx = env->context(); + Local return_obj = Object::New(args.GetIsolate()); + for (auto w : *env->handle_wrap_queue()) { - if (!HandleWrap::HasRef(w)) + if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue; - handle_v.emplace_back(w->GetOwner()); + double async_id = w->get_async_id(); + Local handle_object = w->object(); + USE(return_obj->Set(ctx, Number::New(args.GetIsolate(), + async_id), + handle_object)); } - args.GetReturnValue().Set( - Array::New(env->isolate(), handle_v.data(), handle_v.size())); + + args.GetReturnValue().Set(return_obj); } static void ResourceUsage(const FunctionCallbackInfo& args) { diff --git a/test/parallel/test-handle-wrap-isrefed.js b/test/parallel/test-handle-wrap-isrefed.js index 2fb766ce72ea4b..508e9409c63e06 100644 --- a/test/parallel/test-handle-wrap-isrefed.js +++ b/test/parallel/test-handle-wrap-isrefed.js @@ -4,6 +4,7 @@ const common = require('../common'); const strictEqual = require('assert').strictEqual; const { internalBinding } = require('internal/test/binding'); +const { getActiveResources } = require('util'); // child_process { @@ -107,4 +108,25 @@ const { kStateSymbol } = require('internal/dgram'); } +// timers +{ + const { Timeout } = require('internal/timers'); + strictEqual(Object.values(getActiveResources()).filter( + (handle) => (handle instanceof Timeout)).length, 0); + const timer = setTimeout(() => {}, 500); + const handles = Object.values(getActiveResources()).filter( + (handle) => (handle instanceof Timeout)); + strictEqual(handles.length, 1); + const handle = handles[0]; + strictEqual(Object.getPrototypeOf(handle).hasOwnProperty('hasRef'), + true, 'timer: hasRef() missing'); + strictEqual(handle.hasRef(), true); + timer.unref(); + strictEqual(handle.hasRef(), + false, 'timer: unref() ineffective'); + timer.ref(); + strictEqual(handle.hasRef(), + true, 'timer: ref() ineffective'); +} + // See also test/pseudo-tty/test-handle-wrap-isrefed-tty.js diff --git a/test/parallel/test-process-getactiverequests.js b/test/parallel/test-util-getactiveresources-requests.js similarity index 62% rename from test/parallel/test-process-getactiverequests.js rename to test/parallel/test-util-getactiveresources-requests.js index ed3c0c8fe861ec..ed7834a2922493 100644 --- a/test/parallel/test-process-getactiverequests.js +++ b/test/parallel/test-util-getactiveresources-requests.js @@ -3,8 +3,9 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); +const { getActiveResources } = require('util'); for (let i = 0; i < 12; i++) fs.open(__filename, 'r', common.mustCall()); -assert.strictEqual(process._getActiveRequests().length, 12); +assert.strictEqual(Object.values(getActiveResources()).length, 12); diff --git a/test/parallel/test-process-getactivehandles.js b/test/parallel/test-util-getactiveresources.js similarity index 75% rename from test/parallel/test-process-getactivehandles.js rename to test/parallel/test-util-getactiveresources.js index 2db3da3c563e6e..64f747d857ba91 100644 --- a/test/parallel/test-process-getactivehandles.js +++ b/test/parallel/test-util-getactiveresources.js @@ -3,6 +3,8 @@ require('../common'); const assert = require('assert'); const net = require('net'); +const { getActiveResources } = require('util'); + const NUM = 8; const connections = []; const clients = []; @@ -30,18 +32,18 @@ function clientConnected(client) { function checkAll() { - const handles = process._getActiveHandles(); + const handles = Object.values(getActiveResources()); clients.forEach(function(item) { - assert.ok(handles.includes(item)); + assert.ok(handles.includes(item._handle)); item.destroy(); }); connections.forEach(function(item) { - assert.ok(handles.includes(item)); + assert.ok(handles.includes(item._handle)); item.end(); }); - assert.ok(handles.includes(server)); + assert.ok(handles.includes(server._handle)); server.close(); } diff --git a/test/pseudo-tty/ref_keeps_node_running.js b/test/pseudo-tty/ref_keeps_node_running.js index 52761c140eddac..98f1930c6b7e5c 100644 --- a/test/pseudo-tty/ref_keeps_node_running.js +++ b/test/pseudo-tty/ref_keeps_node_running.js @@ -6,6 +6,7 @@ require('../common'); const { internalBinding } = require('internal/test/binding'); const { TTY, isTTY } = internalBinding('tty_wrap'); const strictEqual = require('assert').strictEqual; +const { getActiveResources } = require('util'); strictEqual(isTTY(0), true, 'fd 0 is not a TTY'); @@ -14,7 +15,8 @@ handle.readStart(); handle.onread = () => {}; function isHandleActive(handle) { - return process._getActiveHandles().some((active) => active === handle); + return Object.values(getActiveResources()) + .some((active) => active === handle); } strictEqual(isHandleActive(handle), true, 'TTY handle not initially active'); From 5cd12af364c920100a0cf797855c71eb49be66fe Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Mon, 19 Apr 2021 20:06:56 +0530 Subject: [PATCH 02/11] fixup! util: add getActiveResources --- lib/internal/timers.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/internal/timers.js b/lib/internal/timers.js index 4e6caa18de5f05..9aa572989c8cd1 100644 --- a/lib/internal/timers.js +++ b/lib/internal/timers.js @@ -284,11 +284,11 @@ ImmediateList.prototype.append = function(item) { // Removes an item from the linked list, adjusting the pointers of adjacent // items and the linked list's head or tail pointers as necessary ImmediateList.prototype.remove = function(item) { - if (item._idleNext !== null) { + if (item._idleNext) { item._idleNext._idlePrev = item._idlePrev; } - if (item._idlePrev !== null) { + if (item._idlePrev) { item._idlePrev._idleNext = item._idleNext; } From 866f94e22aa4fe1bf9bde1fa077c350dd74d81ee Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Tue, 20 Apr 2021 21:00:31 +0530 Subject: [PATCH 03/11] fixup! util: add getActiveResources --- src/node_process_methods.cc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index c3911205036c16..fd6a83151b7aef 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -36,6 +36,7 @@ typedef int mode_t; namespace node { using v8::ApiObject; +using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; using v8::CFunction; @@ -254,7 +255,7 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local ctx = env->context(); - Local return_obj = Object::New(args.GetIsolate()); + Local return_array = Array::New(args.GetIsolate()); for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) { AsyncWrap* w = req_wrap->GetAsyncWrap(); @@ -263,12 +264,12 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { double async_id = w->get_async_id(); Local req_obj = w->object(); - USE(return_obj->Set(ctx, - Number::New(args.GetIsolate(), async_id), - req_obj)); + USE(return_array->Set(ctx, + Number::New(args.GetIsolate(), async_id), + req_obj)); } - args.GetReturnValue().Set(return_obj); + args.GetReturnValue().Set(return_array); } // Non-static, friend of HandleWrap. Could have been a HandleWrap method but @@ -277,19 +278,19 @@ void GetActiveHandles(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local ctx = env->context(); - Local return_obj = Object::New(args.GetIsolate()); + Local return_array = Array::New(args.GetIsolate()); for (auto w : *env->handle_wrap_queue()) { if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue; double async_id = w->get_async_id(); Local handle_object = w->object(); - USE(return_obj->Set(ctx, Number::New(args.GetIsolate(), - async_id), - handle_object)); + USE(return_array->Set(ctx, Number::New(args.GetIsolate(), + async_id), + handle_object)); } - args.GetReturnValue().Set(return_obj); + args.GetReturnValue().Set(return_array); } static void ResourceUsage(const FunctionCallbackInfo& args) { From 410984eb5ea71be5bc6a72a21d32a1db46128452 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Thu, 22 Apr 2021 19:33:03 +0530 Subject: [PATCH 04/11] Revert "fixup! util: add getActiveResources" This reverts commit 866f94e22aa4fe1bf9bde1fa077c350dd74d81ee. --- src/node_process_methods.cc | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/node_process_methods.cc b/src/node_process_methods.cc index fd6a83151b7aef..c3911205036c16 100644 --- a/src/node_process_methods.cc +++ b/src/node_process_methods.cc @@ -36,7 +36,6 @@ typedef int mode_t; namespace node { using v8::ApiObject; -using v8::Array; using v8::ArrayBuffer; using v8::BackingStore; using v8::CFunction; @@ -255,7 +254,7 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local ctx = env->context(); - Local return_array = Array::New(args.GetIsolate()); + Local return_obj = Object::New(args.GetIsolate()); for (ReqWrapBase* req_wrap : *env->req_wrap_queue()) { AsyncWrap* w = req_wrap->GetAsyncWrap(); @@ -264,12 +263,12 @@ static void GetActiveRequests(const FunctionCallbackInfo& args) { double async_id = w->get_async_id(); Local req_obj = w->object(); - USE(return_array->Set(ctx, - Number::New(args.GetIsolate(), async_id), - req_obj)); + USE(return_obj->Set(ctx, + Number::New(args.GetIsolate(), async_id), + req_obj)); } - args.GetReturnValue().Set(return_array); + args.GetReturnValue().Set(return_obj); } // Non-static, friend of HandleWrap. Could have been a HandleWrap method but @@ -278,19 +277,19 @@ void GetActiveHandles(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); Local ctx = env->context(); - Local return_array = Array::New(args.GetIsolate()); + Local return_obj = Object::New(args.GetIsolate()); for (auto w : *env->handle_wrap_queue()) { if (w->persistent().IsEmpty() || !HandleWrap::HasRef(w)) continue; double async_id = w->get_async_id(); Local handle_object = w->object(); - USE(return_array->Set(ctx, Number::New(args.GetIsolate(), - async_id), - handle_object)); + USE(return_obj->Set(ctx, Number::New(args.GetIsolate(), + async_id), + handle_object)); } - args.GetReturnValue().Set(return_array); + args.GetReturnValue().Set(return_obj); } static void ResourceUsage(const FunctionCallbackInfo& args) { From 06ffb79df16cc21ab7889a9166d0f9b5b89e1063 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Thu, 22 Apr 2021 19:37:54 +0530 Subject: [PATCH 05/11] fixup! test: use Object.values for process._get* --- test/pummel/test-net-connect-econnrefused.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/pummel/test-net-connect-econnrefused.js b/test/pummel/test-net-connect-econnrefused.js index 4fd4f8b6943e3c..98f6053a35338a 100644 --- a/test/pummel/test-net-connect-econnrefused.js +++ b/test/pummel/test-net-connect-econnrefused.js @@ -54,8 +54,8 @@ function pummel() { function check() { setTimeout(common.mustCall(function() { - assert.strictEqual(process._getActiveRequests().length, 0); - const activeHandles = process._getActiveHandles(); + assert.strictEqual(Object.values(process._getActiveRequests()).length, 0); + const activeHandles = Object.values(process._getActiveHandles()); assert.ok(activeHandles.every((val) => val.constructor.name !== 'Socket')); }), 0); } From 3c40a73c8ce878ea9bc90c817cbeef1baa733689 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Thu, 22 Apr 2021 20:05:56 +0530 Subject: [PATCH 06/11] util -> async_hooks --- lib/async_hooks.js | 2 + lib/internal/async_hooks.js | 42 +++++++++++++++++++ lib/internal/util.js | 40 ------------------ lib/util.js | 2 - ...sync-hooks-getactiveresources-requests.js} | 2 +- ...=> test-async-hooks-getactiveresources.js} | 2 +- test/parallel/test-handle-wrap-isrefed.js | 2 +- test/pseudo-tty/ref_keeps_node_running.js | 2 +- 8 files changed, 48 insertions(+), 46 deletions(-) rename test/parallel/{test-util-getactiveresources-requests.js => test-async-hooks-getactiveresources-requests.js} (82%) rename test/parallel/{test-util-getactiveresources.js => test-async-hooks-getactiveresources.js} (94%) diff --git a/lib/async_hooks.js b/lib/async_hooks.js index 13c32065fc9188..ca2296a485d7e2 100644 --- a/lib/async_hooks.js +++ b/lib/async_hooks.js @@ -31,6 +31,7 @@ const { registerDestroyHook } = internal_async_hooks; const { executionAsyncId, triggerAsyncId, + getActiveResources, // Private API hasAsyncIdStack, getHookArrays, @@ -347,6 +348,7 @@ module.exports = { createHook, executionAsyncId, triggerAsyncId, + getActiveResources, executionAsyncResource, // Embedder API AsyncResource, diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index b8955f644d15d7..62be66399c9e58 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,10 +1,12 @@ 'use strict'; const { + ArrayPrototypeForEach, ArrayPrototypeSlice, ErrorCaptureStackTrace, ObjectPrototypeHasOwnProperty, ObjectDefineProperty, + ObjectValues, Promise, Symbol, } = primordials; @@ -544,9 +546,49 @@ function triggerAsyncId() { } +const { + _getActiveHandles, + _getActiveRequests, +} = internalBinding('process_methods'); + +let internalTimers; + +function getActiveResources() { + if (internalTimers == null) { + internalTimers = require('internal/timers'); + } + const handles = _getActiveHandles(); + const reqs = _getActiveRequests(); + + const timers = {}; + ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => { + let timer = list._idlePrev === list ? null : list._idlePrev; + + while (timer !== null) { + timers[timer[internalTimers.async_id_symbol]] = timer; + + timer = timer._idlePrev === list ? null : list._idlePrev; + } + }); + + const immediates = {}; + const queue = internalTimers.outstandingQueue.head !== null ? + internalTimers.outstandingQueue : internalTimers.immediateQueue; + let immediate = queue.head; + while (immediate !== null) { + immediates[immediate[internalTimers.async_id_symbol]] = immediate; + + immediate = immediate._idleNext; + } + + return { ...handles, ...reqs, ...timers, ...immediates }; +} + + module.exports = { executionAsyncId, triggerAsyncId, + getActiveResources, // Private API getHookArrays, symbols: { diff --git a/lib/internal/util.js b/lib/internal/util.js index 69bdd740327022..f77f1fc43368c4 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -3,7 +3,6 @@ const { ArrayFrom, ArrayIsArray, - ArrayPrototypeForEach, ArrayPrototypePush, ArrayPrototypeSlice, ArrayPrototypeSort, @@ -15,7 +14,6 @@ const { ObjectGetOwnPropertyDescriptors, ObjectGetPrototypeOf, ObjectSetPrototypeOf, - ObjectValues, Promise, ReflectApply, ReflectConstruct, @@ -47,10 +45,6 @@ const { sleep: _sleep } = internalBinding('util'); const { isNativeError } = internalBinding('types'); -const { - _getActiveHandles, - _getActiveRequests, -} = internalBinding('process_methods'); const noCrypto = !process.versions.openssl; @@ -447,39 +441,6 @@ function createDeferredPromise() { return { promise, resolve, reject }; } -let internalTimers; - -function getActiveResources() { - if (internalTimers == null) { - internalTimers = require('internal/timers'); - } - const handles = _getActiveHandles(); - const reqs = _getActiveRequests(); - - const timers = {}; - ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => { - let timer = list._idlePrev === list ? null : list._idlePrev; - - while (timer !== null) { - timers[timer[internalTimers.async_id_symbol]] = timer; - - timer = timer._idlePrev === list ? null : list._idlePrev; - } - }); - - const immediates = {}; - const queue = internalTimers.outstandingQueue.head !== null ? - internalTimers.outstandingQueue : internalTimers.immediateQueue; - let immediate = queue.head; - while (immediate !== null) { - immediates[immediate[internalTimers.async_id_symbol]] = immediate; - - immediate = immediate._idleNext; - } - - return { ...handles, ...reqs, ...timers, ...immediates }; -} - module.exports = { assertCrypto, cachedResult, @@ -490,7 +451,6 @@ module.exports = { deprecate, emitExperimentalWarning, filterDuplicateStrings, - getActiveResources, getConstructorOf, getSystemErrorMap, getSystemErrorName, diff --git a/lib/util.js b/lib/util.js index 0bce55d315ad98..f3c10bb7720e58 100644 --- a/lib/util.js +++ b/lib/util.js @@ -67,7 +67,6 @@ const types = require('internal/util/types'); const { deprecate, - getActiveResources, getSystemErrorMap, getSystemErrorName: internalErrorName, promisify @@ -258,7 +257,6 @@ module.exports = { deprecate, format, formatWithOptions, - getActiveResources, getSystemErrorMap, getSystemErrorName, inherits, diff --git a/test/parallel/test-util-getactiveresources-requests.js b/test/parallel/test-async-hooks-getactiveresources-requests.js similarity index 82% rename from test/parallel/test-util-getactiveresources-requests.js rename to test/parallel/test-async-hooks-getactiveresources-requests.js index ed7834a2922493..654febbba37398 100644 --- a/test/parallel/test-util-getactiveresources-requests.js +++ b/test/parallel/test-async-hooks-getactiveresources-requests.js @@ -3,7 +3,7 @@ const common = require('../common'); const assert = require('assert'); const fs = require('fs'); -const { getActiveResources } = require('util'); +const { getActiveResources } = require('async_hooks'); for (let i = 0; i < 12; i++) fs.open(__filename, 'r', common.mustCall()); diff --git a/test/parallel/test-util-getactiveresources.js b/test/parallel/test-async-hooks-getactiveresources.js similarity index 94% rename from test/parallel/test-util-getactiveresources.js rename to test/parallel/test-async-hooks-getactiveresources.js index 64f747d857ba91..1787b1c3a7e4d5 100644 --- a/test/parallel/test-util-getactiveresources.js +++ b/test/parallel/test-async-hooks-getactiveresources.js @@ -3,7 +3,7 @@ require('../common'); const assert = require('assert'); const net = require('net'); -const { getActiveResources } = require('util'); +const { getActiveResources } = require('async_hooks'); const NUM = 8; const connections = []; diff --git a/test/parallel/test-handle-wrap-isrefed.js b/test/parallel/test-handle-wrap-isrefed.js index 508e9409c63e06..6098db9fa9952a 100644 --- a/test/parallel/test-handle-wrap-isrefed.js +++ b/test/parallel/test-handle-wrap-isrefed.js @@ -4,7 +4,7 @@ const common = require('../common'); const strictEqual = require('assert').strictEqual; const { internalBinding } = require('internal/test/binding'); -const { getActiveResources } = require('util'); +const { getActiveResources } = require('async_hooks'); // child_process { diff --git a/test/pseudo-tty/ref_keeps_node_running.js b/test/pseudo-tty/ref_keeps_node_running.js index 98f1930c6b7e5c..7f34319c475458 100644 --- a/test/pseudo-tty/ref_keeps_node_running.js +++ b/test/pseudo-tty/ref_keeps_node_running.js @@ -6,7 +6,7 @@ require('../common'); const { internalBinding } = require('internal/test/binding'); const { TTY, isTTY } = internalBinding('tty_wrap'); const strictEqual = require('assert').strictEqual; -const { getActiveResources } = require('util'); +const { getActiveResources } = require('async_hooks'); strictEqual(isTTY(0), true, 'fd 0 is not a TTY'); From e6990a49224491f6edb5f67698dca5f1248b9920 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Fri, 23 Apr 2021 21:20:51 +0530 Subject: [PATCH 07/11] summary --- lib/internal/async_hooks.js | 110 +++++++++++++++--- ...async-hooks-getactiveresources-handles.js} | 10 +- ...async-hooks-getactiveresources-requests.js | 2 +- test/parallel/test-handle-wrap-isrefed.js | 24 ++-- test/pseudo-tty/ref_keeps_node_running.js | 29 +++-- test/pummel/test-net-connect-econnrefused.js | 6 +- 6 files changed, 129 insertions(+), 52 deletions(-) rename test/parallel/{test-async-hooks-getactiveresources.js => test-async-hooks-getactiveresources-handles.js} (80%) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 62be66399c9e58..fbe8fb8dcabc41 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -551,37 +551,109 @@ const { _getActiveRequests, } = internalBinding('process_methods'); +function getSummaryOf(source) { + const summary = {}; + const allowedTypes = [ + 'bigint', + 'boolean', + 'number', + 'string', + ]; + + for (const asyncId in source) { + const object = source[asyncId]; + const eachSummary = { + type: object.constructor.name + }; + + for (const key of Reflect.ownKeys(object)) { + const value = object[key]; + + if (allowedTypes.includes(typeof value)) { + eachSummary[key] = value; + } + } + + summary[asyncId] = eachSummary; + } + + return summary; +} + let internalTimers; -function getActiveResources() { +function getActiveResources(filters) { if (internalTimers == null) { internalTimers = require('internal/timers'); } - const handles = _getActiveHandles(); - const reqs = _getActiveRequests(); - const timers = {}; - ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => { - let timer = list._idlePrev === list ? null : list._idlePrev; + if (!Array.isArray(filters)) { + filters = [ + 'handles', + 'requests', + 'timeouts', + 'immediates', + ]; + } - while (timer !== null) { - timers[timer[internalTimers.async_id_symbol]] = timer; + let activeResources = {}; - timer = timer._idlePrev === list ? null : list._idlePrev; - } - }); + if (filters.includes('handles')) { + const handles = _getActiveHandles(); + + activeResources = { + ...activeResources, + ...getSummaryOf(handles), + }; + } + + if (filters.includes('requests')) { + const reqs = _getActiveRequests(); - const immediates = {}; - const queue = internalTimers.outstandingQueue.head !== null ? - internalTimers.outstandingQueue : internalTimers.immediateQueue; - let immediate = queue.head; - while (immediate !== null) { - immediates[immediate[internalTimers.async_id_symbol]] = immediate; + activeResources = { + ...activeResources, + ...getSummaryOf(reqs), + }; + } + + if (filters.includes('timeouts')) { + const timeouts = {}; + + ArrayPrototypeForEach(ObjectValues(internalTimers.timerListMap), (list) => { + let timeout = list._idlePrev === list ? null : list._idlePrev; + + while (timeout !== null) { + timeouts[timeout[internalTimers.async_id_symbol]] = timeout; + + timeout = timeout._idlePrev === list ? null : list._idlePrev; + } + }); + + activeResources = { + ...activeResources, + ...getSummaryOf(timeouts), + }; + } + + if (filters.includes('immediates')) { + const immediates = {}; + const queue = internalTimers.outstandingQueue.head !== null ? + internalTimers.outstandingQueue : internalTimers.immediateQueue; + let immediate = queue.head; + + while (immediate !== null) { + immediates[immediate[internalTimers.async_id_symbol]] = immediate; + + immediate = immediate._idleNext; + } - immediate = immediate._idleNext; + activeResources = { + ...activeResources, + ...getSummaryOf(immediates), + }; } - return { ...handles, ...reqs, ...timers, ...immediates }; + return activeResources; } diff --git a/test/parallel/test-async-hooks-getactiveresources.js b/test/parallel/test-async-hooks-getactiveresources-handles.js similarity index 80% rename from test/parallel/test-async-hooks-getactiveresources.js rename to test/parallel/test-async-hooks-getactiveresources-handles.js index 1787b1c3a7e4d5..ed0f88001f8d98 100644 --- a/test/parallel/test-async-hooks-getactiveresources.js +++ b/test/parallel/test-async-hooks-getactiveresources-handles.js @@ -32,18 +32,20 @@ function clientConnected(client) { function checkAll() { - const handles = Object.values(getActiveResources()); + const handles = Object.values(getActiveResources('handles')); + + assert.strictEqual( + handles.length, + clients.length + connections.length + [server].length + ); clients.forEach(function(item) { - assert.ok(handles.includes(item._handle)); item.destroy(); }); connections.forEach(function(item) { - assert.ok(handles.includes(item._handle)); item.end(); }); - assert.ok(handles.includes(server._handle)); server.close(); } diff --git a/test/parallel/test-async-hooks-getactiveresources-requests.js b/test/parallel/test-async-hooks-getactiveresources-requests.js index 654febbba37398..2405507380bec0 100644 --- a/test/parallel/test-async-hooks-getactiveresources-requests.js +++ b/test/parallel/test-async-hooks-getactiveresources-requests.js @@ -8,4 +8,4 @@ const { getActiveResources } = require('async_hooks'); for (let i = 0; i < 12; i++) fs.open(__filename, 'r', common.mustCall()); -assert.strictEqual(Object.values(getActiveResources()).length, 12); +assert.strictEqual(Object.values(getActiveResources(['requests'])).length, 12); diff --git a/test/parallel/test-handle-wrap-isrefed.js b/test/parallel/test-handle-wrap-isrefed.js index 6098db9fa9952a..33ef554f1eaab6 100644 --- a/test/parallel/test-handle-wrap-isrefed.js +++ b/test/parallel/test-handle-wrap-isrefed.js @@ -110,23 +110,13 @@ const { kStateSymbol } = require('internal/dgram'); // timers { - const { Timeout } = require('internal/timers'); - strictEqual(Object.values(getActiveResources()).filter( - (handle) => (handle instanceof Timeout)).length, 0); - const timer = setTimeout(() => {}, 500); - const handles = Object.values(getActiveResources()).filter( - (handle) => (handle instanceof Timeout)); - strictEqual(handles.length, 1); - const handle = handles[0]; - strictEqual(Object.getPrototypeOf(handle).hasOwnProperty('hasRef'), - true, 'timer: hasRef() missing'); - strictEqual(handle.hasRef(), true); - timer.unref(); - strictEqual(handle.hasRef(), - false, 'timer: unref() ineffective'); - timer.ref(); - strictEqual(handle.hasRef(), - true, 'timer: ref() ineffective'); + strictEqual(Object.values(getActiveResources('timeouts')).length, 0); + setTimeout(() => {}, 500); + strictEqual(Object.values(getActiveResources('timeouts')).length, 1); + + strictEqual(Object.values(getActiveResources('immediates')).length, 0); + setImmediate(() => {}); + strictEqual(Object.values(getActiveResources('immediates')).length, 1); } // See also test/pseudo-tty/test-handle-wrap-isrefed-tty.js diff --git a/test/pseudo-tty/ref_keeps_node_running.js b/test/pseudo-tty/ref_keeps_node_running.js index 7f34319c475458..081f6cb8d3dc4e 100644 --- a/test/pseudo-tty/ref_keeps_node_running.js +++ b/test/pseudo-tty/ref_keeps_node_running.js @@ -14,19 +14,32 @@ const handle = new TTY(0); handle.readStart(); handle.onread = () => {}; -function isHandleActive(handle) { - return Object.values(getActiveResources()) - .some((active) => active === handle); -} - -strictEqual(isHandleActive(handle), true, 'TTY handle not initially active'); +strictEqual( + Object.values(getActiveResources('handles')).length, + 1, + 'TTY handle not initially active' +); handle.unref(); -strictEqual(isHandleActive(handle), false, 'TTY handle active after unref()'); +strictEqual( + Object.values(getActiveResources('handles')).length, + 1, + 'TTY handle not initially active' +); handle.ref(); -strictEqual(isHandleActive(handle), true, 'TTY handle inactive after ref()'); +strictEqual( + Object.values(getActiveResources('handles')).length, + 2, + 'TTY handle not initially active' +); handle.unref(); + +strictEqual( + Object.values(getActiveResources('handles')).length, + 1, + 'TTY handle not initially active' +); diff --git a/test/pummel/test-net-connect-econnrefused.js b/test/pummel/test-net-connect-econnrefused.js index 98f6053a35338a..7e9b410957823b 100644 --- a/test/pummel/test-net-connect-econnrefused.js +++ b/test/pummel/test-net-connect-econnrefused.js @@ -25,6 +25,7 @@ const common = require('../common'); const assert = require('assert'); const net = require('net'); +const { getActiveRequests } = require('async_hooks'); const ROUNDS = 5; const ATTEMPTS_PER_ROUND = 50; @@ -54,9 +55,8 @@ function pummel() { function check() { setTimeout(common.mustCall(function() { - assert.strictEqual(Object.values(process._getActiveRequests()).length, 0); - const activeHandles = Object.values(process._getActiveHandles()); - assert.ok(activeHandles.every((val) => val.constructor.name !== 'Socket')); + assert.strictEqual(Object.values(getActiveRequests('requests')).length, 0); + assert.strictEqual(Object.values(getActiveRequests('handles')).length, 2); }), 0); } From 2f1f0ae4da7aedfa639b9ea0af9eae5c41cb58aa Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Mon, 26 Apr 2021 19:10:45 +0530 Subject: [PATCH 08/11] fixup! summary --- lib/internal/async_hooks.js | 6 ++++-- test/pseudo-tty/ref_keeps_node_running.js | 6 +++--- test/pummel/test-net-connect-econnrefused.js | 6 +++--- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index fbe8fb8dcabc41..d4e9c97743b5ed 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -1,6 +1,7 @@ 'use strict'; const { + ArrayIsArray, ArrayPrototypeForEach, ArrayPrototypeSlice, ErrorCaptureStackTrace, @@ -8,6 +9,7 @@ const { ObjectDefineProperty, ObjectValues, Promise, + ReflectOwnKeys, Symbol, } = primordials; @@ -566,7 +568,7 @@ function getSummaryOf(source) { type: object.constructor.name }; - for (const key of Reflect.ownKeys(object)) { + for (const key of ReflectOwnKeys(object)) { const value = object[key]; if (allowedTypes.includes(typeof value)) { @@ -587,7 +589,7 @@ function getActiveResources(filters) { internalTimers = require('internal/timers'); } - if (!Array.isArray(filters)) { + if (!ArrayIsArray(filters)) { filters = [ 'handles', 'requests', diff --git a/test/pseudo-tty/ref_keeps_node_running.js b/test/pseudo-tty/ref_keeps_node_running.js index 081f6cb8d3dc4e..215f638d105299 100644 --- a/test/pseudo-tty/ref_keeps_node_running.js +++ b/test/pseudo-tty/ref_keeps_node_running.js @@ -24,7 +24,7 @@ handle.unref(); strictEqual( Object.values(getActiveResources('handles')).length, - 1, + 0, 'TTY handle not initially active' ); @@ -32,7 +32,7 @@ handle.ref(); strictEqual( Object.values(getActiveResources('handles')).length, - 2, + 1, 'TTY handle not initially active' ); @@ -40,6 +40,6 @@ handle.unref(); strictEqual( Object.values(getActiveResources('handles')).length, - 1, + 0, 'TTY handle not initially active' ); diff --git a/test/pummel/test-net-connect-econnrefused.js b/test/pummel/test-net-connect-econnrefused.js index 7e9b410957823b..d899b37455bacc 100644 --- a/test/pummel/test-net-connect-econnrefused.js +++ b/test/pummel/test-net-connect-econnrefused.js @@ -25,7 +25,7 @@ const common = require('../common'); const assert = require('assert'); const net = require('net'); -const { getActiveRequests } = require('async_hooks'); +const { getActiveResources } = require('async_hooks'); const ROUNDS = 5; const ATTEMPTS_PER_ROUND = 50; @@ -55,8 +55,8 @@ function pummel() { function check() { setTimeout(common.mustCall(function() { - assert.strictEqual(Object.values(getActiveRequests('requests')).length, 0); - assert.strictEqual(Object.values(getActiveRequests('handles')).length, 2); + assert.strictEqual(Object.values(getActiveResources('requests')).length, 0); + assert.strictEqual(Object.values(getActiveResources('handles')).length, 2); }), 0); } From 817cdc997bd2da0e054922f94687c9dfa4c32c4f Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Tue, 27 Apr 2021 21:10:20 +0530 Subject: [PATCH 09/11] fixup! fixup! summary --- ...-async-hooks-getactiveresources-handles.js | 4 ++-- test/parallel/test-handle-wrap-isrefed.js | 22 ++++++++++++++----- test/pummel/test-net-connect-econnrefused.js | 10 +++++++-- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/test/parallel/test-async-hooks-getactiveresources-handles.js b/test/parallel/test-async-hooks-getactiveresources-handles.js index ed0f88001f8d98..895c78f462dfc7 100644 --- a/test/parallel/test-async-hooks-getactiveresources-handles.js +++ b/test/parallel/test-async-hooks-getactiveresources-handles.js @@ -34,8 +34,8 @@ function clientConnected(client) { function checkAll() { const handles = Object.values(getActiveResources('handles')); - assert.strictEqual( - handles.length, + assert.ok( + handles.length >= clients.length + connections.length + [server].length ); diff --git a/test/parallel/test-handle-wrap-isrefed.js b/test/parallel/test-handle-wrap-isrefed.js index 33ef554f1eaab6..e8562b1592f87f 100644 --- a/test/parallel/test-handle-wrap-isrefed.js +++ b/test/parallel/test-handle-wrap-isrefed.js @@ -110,13 +110,25 @@ const { kStateSymbol } = require('internal/dgram'); // timers { - strictEqual(Object.values(getActiveResources('timeouts')).length, 0); + strictEqual( + Object.values(getActiveResources('timeouts')). + filter((timeout) => timeout.type === 'Timer'), + 0); setTimeout(() => {}, 500); - strictEqual(Object.values(getActiveResources('timeouts')).length, 1); - - strictEqual(Object.values(getActiveResources('immediates')).length, 0); + strictEqual( + Object.values(getActiveResources('timeouts')). + filter((timeout) => timeout.type === 'Timer'), + 1); + + strictEqual( + Object.values(getActiveResources('immediates')). + filter((immediate) => immediate.type === 'Immediate'), + 0); setImmediate(() => {}); - strictEqual(Object.values(getActiveResources('immediates')).length, 1); + strictEqual( + Object.values(getActiveResources('immediates')). + filter((immediate) => immediate.type === 'Immediate'), + 1); } // See also test/pseudo-tty/test-handle-wrap-isrefed-tty.js diff --git a/test/pummel/test-net-connect-econnrefused.js b/test/pummel/test-net-connect-econnrefused.js index d899b37455bacc..ebba96dd1cc980 100644 --- a/test/pummel/test-net-connect-econnrefused.js +++ b/test/pummel/test-net-connect-econnrefused.js @@ -55,8 +55,14 @@ function pummel() { function check() { setTimeout(common.mustCall(function() { - assert.strictEqual(Object.values(getActiveResources('requests')).length, 0); - assert.strictEqual(Object.values(getActiveResources('handles')).length, 2); + assert.strictEqual( + Object.values(getActiveResources('requests')).length, + 0 + ); + assert.strictEqual( + Object.values(getActiveResources('handles')).length, + 0 + ); }), 0); } From ad5486b9ffedec8cec004f9d0218877a637e1ca8 Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Tue, 27 Apr 2021 21:13:53 +0530 Subject: [PATCH 10/11] allow string filters --- lib/internal/async_hooks.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index d4e9c97743b5ed..151a50d3346342 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -589,7 +589,7 @@ function getActiveResources(filters) { internalTimers = require('internal/timers'); } - if (!ArrayIsArray(filters)) { + if (!ArrayIsArray(filters) && typeof filters !== 'string') { filters = [ 'handles', 'requests', From d115e15677c30a4cdcc5d181daedc673fa1ebfdb Mon Sep 17 00:00:00 2001 From: Darshan Sen Date: Tue, 27 Apr 2021 21:15:34 +0530 Subject: [PATCH 11/11] lint fix --- test/parallel/test-handle-wrap-isrefed.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/parallel/test-handle-wrap-isrefed.js b/test/parallel/test-handle-wrap-isrefed.js index e8562b1592f87f..bfea0b379024bc 100644 --- a/test/parallel/test-handle-wrap-isrefed.js +++ b/test/parallel/test-handle-wrap-isrefed.js @@ -111,23 +111,23 @@ const { kStateSymbol } = require('internal/dgram'); // timers { strictEqual( - Object.values(getActiveResources('timeouts')). - filter((timeout) => timeout.type === 'Timer'), + Object.values(getActiveResources('timeouts')) + .filter((timeout) => timeout.type === 'Timer'), 0); setTimeout(() => {}, 500); strictEqual( - Object.values(getActiveResources('timeouts')). - filter((timeout) => timeout.type === 'Timer'), + Object.values(getActiveResources('timeouts')) + .filter((timeout) => timeout.type === 'Timer'), 1); strictEqual( - Object.values(getActiveResources('immediates')). - filter((immediate) => immediate.type === 'Immediate'), + Object.values(getActiveResources('immediates')) + .filter((immediate) => immediate.type === 'Immediate'), 0); setImmediate(() => {}); strictEqual( - Object.values(getActiveResources('immediates')). - filter((immediate) => immediate.type === 'Immediate'), + Object.values(getActiveResources('immediates')) + .filter((immediate) => immediate.type === 'Immediate'), 1); }