From 1a47a7bbedf971f5d4527d904b2e16c2b9e2cdbd Mon Sep 17 00:00:00 2001 From: theanarkh Date: Tue, 29 Nov 2022 18:58:43 +0800 Subject: [PATCH] diagnostics_channel: fix diagnostics channel memory leak PR-URL: https://github.com/nodejs/node/pull/45633 Reviewed-By: Stephen Belanger Reviewed-By: Yagiz Nizipli Reviewed-By: James M Snell Reviewed-By: Minwoo Jung Reviewed-By: Darshan Sen --- lib/diagnostics_channel.js | 3 +++ src/node_util.cc | 9 +++++++ src/node_util.h | 1 + .../test-diagnostics-channel-memory-leak.js | 24 +++++++++++++++++++ 4 files changed, 37 insertions(+) create mode 100644 test/parallel/test-diagnostics-channel-memory-leak.js diff --git a/lib/diagnostics_channel.js b/lib/diagnostics_channel.js index f8c1edb96dfe8a..9d2d805bf25052 100644 --- a/lib/diagnostics_channel.js +++ b/lib/diagnostics_channel.js @@ -122,6 +122,9 @@ function unsubscribe(name, subscription) { } channels[name].decRef(); + if (channels[name].getRef() === 0) { + delete channels[name]; + } return true; } diff --git a/src/node_util.cc b/src/node_util.cc index 58f58478e59944..f57bbc5605fafa 100644 --- a/src/node_util.cc +++ b/src/node_util.cc @@ -294,6 +294,13 @@ void WeakReference::Get(const FunctionCallbackInfo& args) { args.GetReturnValue().Set(weak_ref->target_.Get(isolate)); } +void WeakReference::GetRef(const FunctionCallbackInfo& args) { + WeakReference* weak_ref = Unwrap(args.Holder()); + Isolate* isolate = args.GetIsolate(); + args.GetReturnValue().Set( + v8::Number::New(isolate, weak_ref->reference_count_)); +} + void WeakReference::IncRef(const FunctionCallbackInfo& args) { WeakReference* weak_ref = Unwrap(args.Holder()); weak_ref->reference_count_++; @@ -391,6 +398,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(ArrayBufferViewHasBuffer); registry->Register(WeakReference::New); registry->Register(WeakReference::Get); + registry->Register(WeakReference::GetRef); registry->Register(WeakReference::IncRef); registry->Register(WeakReference::DecRef); registry->Register(GuessHandleType); @@ -464,6 +472,7 @@ void Initialize(Local target, WeakReference::kInternalFieldCount); weak_ref->Inherit(BaseObject::GetConstructorTemplate(env)); SetProtoMethod(isolate, weak_ref, "get", WeakReference::Get); + SetProtoMethod(isolate, weak_ref, "getRef", WeakReference::GetRef); SetProtoMethod(isolate, weak_ref, "incRef", WeakReference::IncRef); SetProtoMethod(isolate, weak_ref, "decRef", WeakReference::DecRef); SetConstructorFunction(context, target, "WeakReference", weak_ref); diff --git a/src/node_util.h b/src/node_util.h index a68d551fdb481c..616b8c003b2d0d 100644 --- a/src/node_util.h +++ b/src/node_util.h @@ -23,6 +23,7 @@ class WeakReference : public SnapshotableObject { v8::Local target); static void New(const v8::FunctionCallbackInfo& args); static void Get(const v8::FunctionCallbackInfo& args); + static void GetRef(const v8::FunctionCallbackInfo& args); static void IncRef(const v8::FunctionCallbackInfo& args); static void DecRef(const v8::FunctionCallbackInfo& args); diff --git a/test/parallel/test-diagnostics-channel-memory-leak.js b/test/parallel/test-diagnostics-channel-memory-leak.js new file mode 100644 index 00000000000000..9e6364d168562f --- /dev/null +++ b/test/parallel/test-diagnostics-channel-memory-leak.js @@ -0,0 +1,24 @@ +// Flags: --expose-gc +'use strict'; + +// This test ensures that diagnostic channel references aren't leaked. + +require('../common'); +const { ok } = require('assert'); + +const { subscribe, unsubscribe } = require('diagnostics_channel'); + +function noop() {} + +const heapUsedBefore = process.memoryUsage().heapUsed; + +for (let i = 0; i < 1000; i++) { + subscribe(String(i), noop); + unsubscribe(String(i), noop); +} + +global.gc(); + +const heapUsedAfter = process.memoryUsage().heapUsed; + +ok(heapUsedBefore >= heapUsedAfter);