Skip to content

Commit

Permalink
util: add getActiveResources
Browse files Browse the repository at this point in the history
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
  • Loading branch information
RaisinTen committed Apr 19, 2021
1 parent 36decec commit fa990cd
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 23 deletions.
15 changes: 8 additions & 7 deletions lib/internal/timers.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -649,6 +649,7 @@ module.exports = {
setUnrefTimeout,
getTimerDuration,
immediateQueue,
outstandingQueue,
getTimerCallbacks,
immediateInfoFields: {
kCount,
Expand Down
40 changes: 40 additions & 0 deletions lib/internal/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const {
ArrayFrom,
ArrayIsArray,
ArrayPrototypeForEach,
ArrayPrototypePush,
ArrayPrototypeSlice,
ArrayPrototypeSort,
Expand All @@ -14,6 +15,7 @@ const {
ObjectGetOwnPropertyDescriptors,
ObjectGetPrototypeOf,
ObjectSetPrototypeOf,
ObjectValues,
Promise,
ReflectApply,
ReflectConstruct,
Expand Down Expand Up @@ -45,6 +47,10 @@ const {
sleep: _sleep
} = internalBinding('util');
const { isNativeError } = internalBinding('types');
const {
_getActiveHandles,
_getActiveRequests,
} = internalBinding('process_methods');

const noCrypto = !process.versions.openssl;

Expand Down Expand Up @@ -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,
Expand All @@ -451,6 +490,7 @@ module.exports = {
deprecate,
emitExperimentalWarning,
filterDuplicateStrings,
getActiveResources,
getConstructorOf,
getSystemErrorMap,
getSystemErrorName,
Expand Down
2 changes: 2 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const types = require('internal/util/types');

const {
deprecate,
getActiveResources,
getSystemErrorMap,
getSystemErrorName: internalErrorName,
promisify
Expand Down Expand Up @@ -257,6 +258,7 @@ module.exports = {
deprecate,
format,
formatWithOptions,
getActiveResources,
getSystemErrorMap,
getSystemErrorName,
inherits,
Expand Down
32 changes: 22 additions & 10 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"
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -253,31 +253,43 @@ static void Uptime(const FunctionCallbackInfo<Value>& args) {
static void GetActiveRequests(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> request_v;
Local<Context> ctx = env->context();
Local<Object> 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<Object> 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
// implemented here for consistency with GetActiveRequests().
void GetActiveHandles(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);

std::vector<Local<Value>> handle_v;
Local<Context> ctx = env->context();
Local<Object> 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<Object> 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<Value>& args) {
Expand Down
22 changes: 22 additions & 0 deletions test/parallel/test-handle-wrap-isrefed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [];
Expand Down Expand Up @@ -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();
}
4 changes: 3 additions & 1 deletion test/pseudo-tty/ref_keeps_node_running.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand All @@ -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');
Expand Down

0 comments on commit fa990cd

Please sign in to comment.