From 7747720a22c50998be01607b37818ca2707d7fc8 Mon Sep 17 00:00:00 2001 From: pabloerhard Date: Fri, 24 Apr 2026 14:28:07 -0400 Subject: [PATCH 1/4] perf_hooks: add per event loop iteration delay sampling add a samplePerIteration option to monitorEventLoopDelay that records event loop delay from libuv event loop iterations instead of the existing timer interval sampler. The default remains the interval based, uses of monitorEventLoopDelay() will still behave the same unless the samplePerIteration options is passed through. Signed-off-by: Pablo Erhard Hernandez --- doc/api/perf_hooks.md | 25 +-- lib/internal/perf/event_loop_delay.js | 7 +- src/env_properties.h | 1 + src/histogram.cc | 176 ++++++++++++++++++ src/histogram.h | 63 +++++++ src/node_perf.cc | 7 + .../test-performance-eventloopdelay.js | 22 +++ 7 files changed, 288 insertions(+), 13 deletions(-) diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 61634056679062..70b13f8620bf39 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -1707,8 +1707,11 @@ added: v11.10.0 --> * `options` {Object} - * `resolution` {number} The sampling rate in milliseconds. Must be greater - than zero. **Default:** `10`. + * `samplePerIteration` {boolean} When `true`, samples are taken once per + event loop iteration. **Default:** `false`. + * `resolution` {number} The sampling rate in milliseconds for interval-based + sampling. Must be greater than zero. This option is ignored when + `samplePerIteration` is `true`. **Default:** `10`. * Returns: {IntervalHistogram} _This property is an extension by Node.js. It is not available in Web browsers._ @@ -1716,11 +1719,11 @@ _This property is an extension by Node.js. It is not available in Web browsers._ Creates an `IntervalHistogram` object that samples and reports the event loop delay over time. The delays will be reported in nanoseconds. -Using a timer to detect approximate event loop delay works because the -execution of timers is tied specifically to the lifecycle of the libuv -event loop. That is, a delay in the loop will cause a delay in the execution -of the timer, and those delays are specifically what this API is intended to -detect. +By default, the histogram is updated by a timer using the configured +`resolution`. When `samplePerIteration` is `true`, samples are taken once per +event loop iteration using `uv_prepare_t` and `uv_check_t` hooks. In that mode, +the histogram does not keep the loop alive or force additional iterations when +the application is idle. ```mjs import { monitorEventLoopDelay } from 'node:perf_hooks'; @@ -1999,7 +2002,7 @@ The standard deviation of the recorded event loop delays. ## Class: `IntervalHistogram extends Histogram` -A `Histogram` that is periodically updated on a given interval. +A `Histogram` that records event loop delay. ### `histogram.disable()` @@ -2009,7 +2012,7 @@ added: v11.10.0 * Returns: {boolean} -Disables the update interval timer. Returns `true` if the timer was +Disables event loop delay sampling. Returns `true` if sampling was stopped, `false` if it was already stopped. ### `histogram.enable()` @@ -2020,7 +2023,7 @@ added: v11.10.0 * Returns: {boolean} -Enables the update interval timer. Returns `true` if the timer was +Enables event loop delay sampling. Returns `true` if sampling was started, `false` if it was already started. ### `histogram[Symbol.dispose]()` @@ -2029,7 +2032,7 @@ started, `false` if it was already started. added: v24.2.0 --> -Disables the update interval timer when the histogram is disposed. +Disables event loop delay sampling when the histogram is disposed. ```js const { monitorEventLoopDelay } = require('node:perf_hooks'); diff --git a/lib/internal/perf/event_loop_delay.js b/lib/internal/perf/event_loop_delay.js index 17581b1310c5c0..ebf0017b70df85 100644 --- a/lib/internal/perf/event_loop_delay.js +++ b/lib/internal/perf/event_loop_delay.js @@ -18,6 +18,7 @@ const { } = internalBinding('performance'); const { + validateBoolean, validateInteger, validateObject, } = require('internal/validators'); @@ -74,6 +75,7 @@ class ELDHistogram extends Histogram { /** * @param {{ + * samplePerIteration : boolean, * resolution : number * }} [options] * @returns {ELDHistogram} @@ -81,14 +83,15 @@ class ELDHistogram extends Histogram { function monitorEventLoopDelay(options = kEmptyObject) { validateObject(options, 'options'); - const { resolution = 10 } = options; + const { samplePerIteration = false, resolution = 10 } = options; + validateBoolean(samplePerIteration, 'options.samplePerIteration'); validateInteger(resolution, 'options.resolution', 1); return ReflectConstruct( function() { markTransferMode(this, true, false); this[kEnabled] = false; - this[kHandle] = createELDHistogram(resolution); + this[kHandle] = createELDHistogram(resolution, samplePerIteration); this[kMap] = new SafeMap(); }, [], ELDHistogram); } diff --git a/src/env_properties.h b/src/env_properties.h index 6530f89ec918ac..40e7bba383e991 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -431,6 +431,7 @@ V(http2ping_constructor_template, v8::ObjectTemplate) \ V(i18n_converter_template, v8::ObjectTemplate) \ V(intervalhistogram_constructor_template, v8::FunctionTemplate) \ + V(eldhistogram_constructor_template, v8::FunctionTemplate) \ V(iter_template, v8::DictionaryTemplate) \ V(js_transferable_constructor_template, v8::FunctionTemplate) \ V(libuv_stream_wrap_ctor_template, v8::FunctionTemplate) \ diff --git a/src/histogram.cc b/src/histogram.cc index 8752a419ec4030..e6acfdbd967b41 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -68,6 +68,10 @@ CFunction IntervalHistogram::fast_start_( CFunction::Make(&IntervalHistogram::FastStart)); CFunction IntervalHistogram::fast_stop_( CFunction::Make(&IntervalHistogram::FastStop)); +CFunction ELDHistogram::fast_start_( + CFunction::Make(&ELDHistogram::FastStart)); +CFunction ELDHistogram::fast_stop_( + CFunction::Make(&ELDHistogram::FastStop)); void HistogramImpl::AddMethods(Isolate* isolate, Local tmpl) { // TODO(@jasnell): The bigint API variations do not yet support fast @@ -444,6 +448,173 @@ void IntervalHistogram::FastStop(Local receiver) { histogram->OnStop(); } +Local ELDHistogram::GetConstructorTemplate( + Environment* env) { + Local tmpl = env->eldhistogram_constructor_template(); + if (tmpl.IsEmpty()) { + Isolate* isolate = env->isolate(); + tmpl = NewFunctionTemplate(isolate, nullptr); + tmpl->Inherit(HandleWrap::GetConstructorTemplate(env)); + tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "Histogram")); + auto instance = tmpl->InstanceTemplate(); + instance->SetInternalFieldCount(ELDHistogram::kInternalFieldCount); + HistogramImpl::AddMethods(isolate, tmpl); + SetFastMethod(isolate, instance, "start", Start, &fast_start_); + SetFastMethod(isolate, instance, "stop", Stop, &fast_stop_); + env->set_eldhistogram_constructor_template(tmpl); + } + return tmpl; +} + +void ELDHistogram::RegisterExternalReferences( + ExternalReferenceRegistry* registry) { + registry->Register(Start); + registry->Register(Stop); + registry->Register(fast_start_); + registry->Register(fast_stop_); + HistogramImpl::RegisterExternalReferences(registry); +} + +ELDHistogram::ELDHistogram( + Environment* env, + Local wrap, + AsyncWrap::ProviderType type, + const Histogram::Options& options) + : HandleWrap( + env, + wrap, + reinterpret_cast(&check_handle_), + type), + HistogramImpl(options) { + MakeWeak(); + wrap->SetAlignedPointerInInternalField( + HistogramImpl::InternalFields::kImplField, + static_cast(this), + EmbedderDataTag::kDefault); + uv_check_init(env->event_loop(), &check_handle_); + uv_prepare_init(env->event_loop(), &prepare_handle_); + uv_unref(reinterpret_cast(&check_handle_)); + uv_unref(reinterpret_cast(&prepare_handle_)); + prepare_handle_.data = this; +} + +BaseObjectPtr ELDHistogram::Create( + Environment* env, + const Histogram::Options& options) { + Local obj; + if (!GetConstructorTemplate(env) + ->InstanceTemplate() + ->NewInstance(env->context()).ToLocal(&obj)) { + return nullptr; + } + + return MakeBaseObject( + env, + obj, + AsyncWrap::PROVIDER_ELDHISTOGRAM, + options); +} + +void ELDHistogram::PrepareCB(uv_prepare_t* handle) { + ELDHistogram* self = static_cast(handle->data); + if (!self->enabled_) return; + self->prepare_time_ = uv_hrtime(); + self->timeout_ = uv_backend_timeout(handle->loop); +} + +void ELDHistogram::CheckCB(uv_check_t* handle) { + ELDHistogram* self = + ContainerOf(&ELDHistogram::check_handle_, handle); + if (!self->enabled_) return; + + uint64_t check_time = uv_hrtime(); + uint64_t poll_time = check_time - self->prepare_time_; + uint64_t latency = self->prepare_time_ - self->check_time_; + + if (self->timeout_ >= 0) { + uint64_t timeout_ns = static_cast(self->timeout_) * 1000 * 1000; + if (poll_time > timeout_ns) { + latency += poll_time - timeout_ns; + } + } + + self->histogram()->Record(latency == 0 ? 1 : latency); + self->check_time_ = check_time; +} + +void ELDHistogram::MemoryInfo(MemoryTracker* tracker) const { + tracker->TrackField("histogram", histogram()); +} + +void ELDHistogram::OnStart(StartFlags flags) { + if (enabled_ || IsHandleClosing()) return; + enabled_ = true; + if (flags == StartFlags::RESET) + histogram()->Reset(); + check_time_ = uv_hrtime(); + prepare_time_ = check_time_; + timeout_ = 0; + uv_check_start(&check_handle_, CheckCB); + uv_prepare_start(&prepare_handle_, PrepareCB); + uv_unref(reinterpret_cast(&check_handle_)); + uv_unref(reinterpret_cast(&prepare_handle_)); +} + +void ELDHistogram::OnStop() { + if (!enabled_ || IsHandleClosing()) return; + enabled_ = false; + uv_check_stop(&check_handle_); + uv_prepare_stop(&prepare_handle_); +} + +void ELDHistogram::PrepareCloseCB(uv_handle_t* handle) { + ELDHistogram* self = static_cast(handle->data); + uv_close(reinterpret_cast(&self->check_handle_), + HandleWrap::OnClose); +} + +void ELDHistogram::Close(Local close_callback) { + if (IsHandleClosing()) return; + OnStop(); + state_ = kClosing; + + if (!close_callback.IsEmpty() && close_callback->IsFunction() && + !persistent().IsEmpty()) { + object()->Set(env()->context(), + env()->handle_onclose_symbol(), + close_callback).Check(); + } + + uv_close(reinterpret_cast(&prepare_handle_), + PrepareCloseCB); +} + +void ELDHistogram::Start(const FunctionCallbackInfo& args) { + ELDHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, args.This()); + histogram->OnStart(args[0]->IsTrue() ? StartFlags::RESET : StartFlags::NONE); +} + +void ELDHistogram::FastStart(Local receiver, bool reset) { + TRACK_V8_FAST_API_CALL("histogram.start"); + ELDHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + histogram->OnStart(reset ? StartFlags::RESET : StartFlags::NONE); +} + +void ELDHistogram::Stop(const FunctionCallbackInfo& args) { + ELDHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, args.This()); + histogram->OnStop(); +} + +void ELDHistogram::FastStop(Local receiver) { + TRACK_V8_FAST_API_CALL("histogram.stop"); + ELDHistogram* histogram; + ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); + histogram->OnStop(); +} + void HistogramImpl::GetCount(const FunctionCallbackInfo& args) { HistogramImpl* histogram = HistogramImpl::FromJSObject(args.This()); double value = static_cast((*histogram)->Count()); @@ -607,6 +778,11 @@ HistogramImpl* HistogramImpl::FromJSObject(Local value) { HistogramImpl::kImplField, EmbedderDataTag::kDefault)); } +std::unique_ptr +ELDHistogram::CloneForMessaging() const { + return std::make_unique(histogram()); +} + std::unique_ptr IntervalHistogram::CloneForMessaging() const { return std::make_unique(histogram()); diff --git a/src/histogram.h b/src/histogram.h index 31c6564b9b1f12..9952c9b648ec72 100644 --- a/src/histogram.h +++ b/src/histogram.h @@ -266,6 +266,69 @@ class IntervalHistogram final : public HandleWrap, public HistogramImpl { static v8::CFunction fast_stop_; }; +class ELDHistogram final : public HandleWrap, public HistogramImpl { + public: + enum InternalFields { + kInternalFieldCount = std::max( + HandleWrap::kInternalFieldCount, HistogramImpl::kInternalFieldCount), + }; + + enum class StartFlags { + NONE, + RESET + }; + + static void RegisterExternalReferences(ExternalReferenceRegistry* registry); + + static v8::Local GetConstructorTemplate( + Environment* env); + + static BaseObjectPtr Create( + Environment* env, + const Histogram::Options& options); + + ELDHistogram( + Environment* env, + v8::Local wrap, + AsyncWrap::ProviderType type, + const Histogram::Options& options = Histogram::Options {}); + + static void Start(const v8::FunctionCallbackInfo& args); + static void Stop(const v8::FunctionCallbackInfo& args); + + static void FastStart(v8::Local receiver, bool reset); + static void FastStop(v8::Local receiver); + + BaseObject::TransferMode GetTransferMode() const override { + return TransferMode::kCloneable; + } + std::unique_ptr CloneForMessaging() const override; + + void Close(v8::Local close_callback = + v8::Local()) override; + + void MemoryInfo(MemoryTracker* tracker) const override; + SET_MEMORY_INFO_NAME(ELDHistogram) + SET_SELF_SIZE(ELDHistogram) + + private: + static void PrepareCB(uv_prepare_t* handle); + static void CheckCB(uv_check_t* handle); + static void PrepareCloseCB(uv_handle_t* handle); + void OnStart(StartFlags flags = StartFlags::RESET); + void OnStop(); + + bool enabled_ = false; + uv_prepare_t prepare_handle_; + uv_check_t check_handle_; + uint64_t prepare_time_ = 0; + uint64_t check_time_ = 0; + int64_t timeout_ = 0; + + static v8::CFunction fast_start_; + static v8::CFunction fast_stop_; +}; + } // namespace node #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS diff --git a/src/node_perf.cc b/src/node_perf.cc index e984fd4c3bf003..5d2d0c623c9097 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -282,6 +282,12 @@ void CreateELDHistogram(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); int64_t interval = args[0].As()->Value(); CHECK_GT(interval, 0); + if (args[1]->IsTrue()) { + BaseObjectPtr histogram = + ELDHistogram::Create(env, Histogram::Options { 1 }); + args.GetReturnValue().Set(histogram->object()); + return; + } BaseObjectPtr histogram = IntervalHistogram::Create(env, interval, [](Histogram& histogram) { uint64_t delta = histogram.RecordDelta(); @@ -413,6 +419,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(fast_performance_now); HistogramBase::RegisterExternalReferences(registry); IntervalHistogram::RegisterExternalReferences(registry); + ELDHistogram::RegisterExternalReferences(registry); } } // namespace performance } // namespace node diff --git a/test/sequential/test-performance-eventloopdelay.js b/test/sequential/test-performance-eventloopdelay.js index 66493318f5651d..c3a87ac53d3794 100644 --- a/test/sequential/test-performance-eventloopdelay.js +++ b/test/sequential/test-performance-eventloopdelay.js @@ -49,6 +49,16 @@ const { sleep } = require('internal/util'); } ); }); + + [null, 'a', 1, {}, []].forEach((i) => { + assert.throws( + () => monitorEventLoopDelay({ samplePerIteration: i }), + { + name: 'TypeError', + code: 'ERR_INVALID_ARG_TYPE', + } + ); + }); } { @@ -114,6 +124,18 @@ const { sleep } = require('internal/util'); spinAWhile(); } +{ + const histogram = monitorEventLoopDelay({ samplePerIteration: true }); + histogram.enable(); + setTimeout(common.mustCall(() => { + histogram.disable(); + assert(histogram.count > 0, + `Expected samples to be recorded, got count=${histogram.count}`); + assert(histogram.min > 0); + assert(histogram.max > 0); + }), common.platformTimeout(20)); +} + // Make sure that the histogram instances can be garbage-collected without // and not just implicitly destroyed when the Environment is torn down. process.on('exit', global.gc); From 2660e2ec382d165830fd89507648188d4ffc4f59 Mon Sep 17 00:00:00 2001 From: pabloerhard Date: Thu, 30 Apr 2026 13:54:56 -0400 Subject: [PATCH 2/4] perf_hooks: test event loop delay fast api callbacks Add test coverage for the per iteration event loop delay histogram start and stop callbacks Use disitinct debug tracking keys for the event loop delay histogram callbacks. --- src/histogram.cc | 4 +-- ...oks-monitor-event-loop-delay-fast-calls.js | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js diff --git a/src/histogram.cc b/src/histogram.cc index e6acfdbd967b41..6df2fc151f5c09 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -596,7 +596,7 @@ void ELDHistogram::Start(const FunctionCallbackInfo& args) { } void ELDHistogram::FastStart(Local receiver, bool reset) { - TRACK_V8_FAST_API_CALL("histogram.start"); + TRACK_V8_FAST_API_CALL("histogram.eventLoopDelay.start"); ELDHistogram* histogram; ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); histogram->OnStart(reset ? StartFlags::RESET : StartFlags::NONE); @@ -609,7 +609,7 @@ void ELDHistogram::Stop(const FunctionCallbackInfo& args) { } void ELDHistogram::FastStop(Local receiver) { - TRACK_V8_FAST_API_CALL("histogram.stop"); + TRACK_V8_FAST_API_CALL("histogram.eventLoopDelay.stop"); ELDHistogram* histogram; ASSIGN_OR_RETURN_UNWRAP(&histogram, receiver); histogram->OnStop(); diff --git a/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js b/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js new file mode 100644 index 00000000000000..1954f52e441557 --- /dev/null +++ b/test/parallel/test-perf-hooks-monitor-event-loop-delay-fast-calls.js @@ -0,0 +1,26 @@ +// Flags: --allow-natives-syntax --expose-internals --no-warnings +'use strict'; + +const common = require('../common'); +const assert = require('assert'); + +const { internalBinding } = require('internal/test/binding'); +const { createELDHistogram } = internalBinding('performance'); + +const histogram = createELDHistogram(1, true); + +function testFastMethods() { + histogram.start(true); + histogram.stop(); +} + +eval('%PrepareFunctionForOptimization(testFastMethods)'); +testFastMethods(); +eval('%OptimizeFunctionOnNextCall(testFastMethods)'); +testFastMethods(); + +if (common.isDebug) { + const { getV8FastApiCallCount } = internalBinding('debug'); + assert.strictEqual(getV8FastApiCallCount('histogram.eventLoopDelay.start'), 1); + assert.strictEqual(getV8FastApiCallCount('histogram.eventLoopDelay.stop'), 1); +} From 89a5c17f2400293bcec4d8d84c345ccd96b52b71 Mon Sep 17 00:00:00 2001 From: pabloerhard Date: Thu, 30 Apr 2026 19:33:22 -0400 Subject: [PATCH 3/4] perf_hooks: format cpp --- src/histogram.cc | 54 ++++++++++++++++++------------------------------ src/histogram.h | 23 ++++++++------------- src/node_perf.cc | 2 +- 3 files changed, 30 insertions(+), 49 deletions(-) diff --git a/src/histogram.cc b/src/histogram.cc index 6df2fc151f5c09..77bb98a408da95 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -68,10 +68,8 @@ CFunction IntervalHistogram::fast_start_( CFunction::Make(&IntervalHistogram::FastStart)); CFunction IntervalHistogram::fast_stop_( CFunction::Make(&IntervalHistogram::FastStop)); -CFunction ELDHistogram::fast_start_( - CFunction::Make(&ELDHistogram::FastStart)); -CFunction ELDHistogram::fast_stop_( - CFunction::Make(&ELDHistogram::FastStop)); +CFunction ELDHistogram::fast_start_(CFunction::Make(&ELDHistogram::FastStart)); +CFunction ELDHistogram::fast_stop_(CFunction::Make(&ELDHistogram::FastStop)); void HistogramImpl::AddMethods(Isolate* isolate, Local tmpl) { // TODO(@jasnell): The bigint API variations do not yet support fast @@ -448,8 +446,7 @@ void IntervalHistogram::FastStop(Local receiver) { histogram->OnStop(); } -Local ELDHistogram::GetConstructorTemplate( - Environment* env) { +Local ELDHistogram::GetConstructorTemplate(Environment* env) { Local tmpl = env->eldhistogram_constructor_template(); if (tmpl.IsEmpty()) { Isolate* isolate = env->isolate(); @@ -475,16 +472,12 @@ void ELDHistogram::RegisterExternalReferences( HistogramImpl::RegisterExternalReferences(registry); } -ELDHistogram::ELDHistogram( - Environment* env, - Local wrap, - AsyncWrap::ProviderType type, - const Histogram::Options& options) +ELDHistogram::ELDHistogram(Environment* env, + Local wrap, + AsyncWrap::ProviderType type, + const Histogram::Options& options) : HandleWrap( - env, - wrap, - reinterpret_cast(&check_handle_), - type), + env, wrap, reinterpret_cast(&check_handle_), type), HistogramImpl(options) { MakeWeak(); wrap->SetAlignedPointerInInternalField( @@ -499,20 +492,17 @@ ELDHistogram::ELDHistogram( } BaseObjectPtr ELDHistogram::Create( - Environment* env, - const Histogram::Options& options) { + Environment* env, const Histogram::Options& options) { Local obj; if (!GetConstructorTemplate(env) - ->InstanceTemplate() - ->NewInstance(env->context()).ToLocal(&obj)) { + ->InstanceTemplate() + ->NewInstance(env->context()) + .ToLocal(&obj)) { return nullptr; } return MakeBaseObject( - env, - obj, - AsyncWrap::PROVIDER_ELDHISTOGRAM, - options); + env, obj, AsyncWrap::PROVIDER_ELDHISTOGRAM, options); } void ELDHistogram::PrepareCB(uv_prepare_t* handle) { @@ -523,8 +513,7 @@ void ELDHistogram::PrepareCB(uv_prepare_t* handle) { } void ELDHistogram::CheckCB(uv_check_t* handle) { - ELDHistogram* self = - ContainerOf(&ELDHistogram::check_handle_, handle); + ELDHistogram* self = ContainerOf(&ELDHistogram::check_handle_, handle); if (!self->enabled_) return; uint64_t check_time = uv_hrtime(); @@ -549,8 +538,7 @@ void ELDHistogram::MemoryInfo(MemoryTracker* tracker) const { void ELDHistogram::OnStart(StartFlags flags) { if (enabled_ || IsHandleClosing()) return; enabled_ = true; - if (flags == StartFlags::RESET) - histogram()->Reset(); + if (flags == StartFlags::RESET) histogram()->Reset(); check_time_ = uv_hrtime(); prepare_time_ = check_time_; timeout_ = 0; @@ -580,13 +568,12 @@ void ELDHistogram::Close(Local close_callback) { if (!close_callback.IsEmpty() && close_callback->IsFunction() && !persistent().IsEmpty()) { - object()->Set(env()->context(), - env()->handle_onclose_symbol(), - close_callback).Check(); + object() + ->Set(env()->context(), env()->handle_onclose_symbol(), close_callback) + .Check(); } - uv_close(reinterpret_cast(&prepare_handle_), - PrepareCloseCB); + uv_close(reinterpret_cast(&prepare_handle_), PrepareCloseCB); } void ELDHistogram::Start(const FunctionCallbackInfo& args) { @@ -778,8 +765,7 @@ HistogramImpl* HistogramImpl::FromJSObject(Local value) { HistogramImpl::kImplField, EmbedderDataTag::kDefault)); } -std::unique_ptr -ELDHistogram::CloneForMessaging() const { +std::unique_ptr ELDHistogram::CloneForMessaging() const { return std::make_unique(histogram()); } diff --git a/src/histogram.h b/src/histogram.h index 9952c9b648ec72..0fec31d447b631 100644 --- a/src/histogram.h +++ b/src/histogram.h @@ -273,25 +273,20 @@ class ELDHistogram final : public HandleWrap, public HistogramImpl { HandleWrap::kInternalFieldCount, HistogramImpl::kInternalFieldCount), }; - enum class StartFlags { - NONE, - RESET - }; + enum class StartFlags { NONE, RESET }; static void RegisterExternalReferences(ExternalReferenceRegistry* registry); static v8::Local GetConstructorTemplate( Environment* env); - static BaseObjectPtr Create( - Environment* env, - const Histogram::Options& options); + static BaseObjectPtr Create(Environment* env, + const Histogram::Options& options); - ELDHistogram( - Environment* env, - v8::Local wrap, - AsyncWrap::ProviderType type, - const Histogram::Options& options = Histogram::Options {}); + ELDHistogram(Environment* env, + v8::Local wrap, + AsyncWrap::ProviderType type, + const Histogram::Options& options = Histogram::Options{}); static void Start(const v8::FunctionCallbackInfo& args); static void Stop(const v8::FunctionCallbackInfo& args); @@ -304,8 +299,8 @@ class ELDHistogram final : public HandleWrap, public HistogramImpl { } std::unique_ptr CloneForMessaging() const override; - void Close(v8::Local close_callback = - v8::Local()) override; + void Close( + v8::Local close_callback = v8::Local()) override; void MemoryInfo(MemoryTracker* tracker) const override; SET_MEMORY_INFO_NAME(ELDHistogram) diff --git a/src/node_perf.cc b/src/node_perf.cc index 5d2d0c623c9097..198fe136f38c20 100644 --- a/src/node_perf.cc +++ b/src/node_perf.cc @@ -284,7 +284,7 @@ void CreateELDHistogram(const FunctionCallbackInfo& args) { CHECK_GT(interval, 0); if (args[1]->IsTrue()) { BaseObjectPtr histogram = - ELDHistogram::Create(env, Histogram::Options { 1 }); + ELDHistogram::Create(env, Histogram::Options{1}); args.GetReturnValue().Set(histogram->object()); return; } From 3b216177c5c035c59dea16a27844d2102a115cc0 Mon Sep 17 00:00:00 2001 From: pabloerhard Date: Tue, 19 May 2026 17:20:45 -0400 Subject: [PATCH 4/4] refactor: use super Close method Use the super Close method instead of duplicating code --- src/histogram.cc | 18 ++---------------- src/histogram.h | 1 - 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/histogram.cc b/src/histogram.cc index 77bb98a408da95..9bac52b3be286f 100644 --- a/src/histogram.cc +++ b/src/histogram.cc @@ -555,25 +555,11 @@ void ELDHistogram::OnStop() { uv_prepare_stop(&prepare_handle_); } -void ELDHistogram::PrepareCloseCB(uv_handle_t* handle) { - ELDHistogram* self = static_cast(handle->data); - uv_close(reinterpret_cast(&self->check_handle_), - HandleWrap::OnClose); -} - void ELDHistogram::Close(Local close_callback) { if (IsHandleClosing()) return; OnStop(); - state_ = kClosing; - - if (!close_callback.IsEmpty() && close_callback->IsFunction() && - !persistent().IsEmpty()) { - object() - ->Set(env()->context(), env()->handle_onclose_symbol(), close_callback) - .Check(); - } - - uv_close(reinterpret_cast(&prepare_handle_), PrepareCloseCB); + HandleWrap::Close(close_callback); + uv_close(reinterpret_cast(&prepare_handle_), nullptr); } void ELDHistogram::Start(const FunctionCallbackInfo& args) { diff --git a/src/histogram.h b/src/histogram.h index 0fec31d447b631..836b784b202b3e 100644 --- a/src/histogram.h +++ b/src/histogram.h @@ -309,7 +309,6 @@ class ELDHistogram final : public HandleWrap, public HistogramImpl { private: static void PrepareCB(uv_prepare_t* handle); static void CheckCB(uv_check_t* handle); - static void PrepareCloseCB(uv_handle_t* handle); void OnStart(StartFlags flags = StartFlags::RESET); void OnStop();