Skip to content

Commit d8c3ecc

Browse files
apapirovskiMylesBorins
authored andcommitted
perf_hooks: fix scheduling regression
Scheduling a PerformanceGCCallback should not keep the loop alive but due to the recent switch to using the native SetImmediate method, it does. Go back to using uv_async_t and add a regression test. PR-URL: #18051 Fixes: #18047 Refs: #18020 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
1 parent b046412 commit d8c3ecc

File tree

2 files changed

+27
-7
lines changed

2 files changed

+27
-7
lines changed

src/node_perf.cc

+17-7
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
184184
}
185185

186186
// Creates a GC Performance Entry and passes it to observers
187-
void PerformanceGCCallback(Environment* env, void* ptr) {
188-
GCPerformanceEntry* entry = static_cast<GCPerformanceEntry*>(ptr);
187+
void PerformanceGCCallback(uv_async_t* handle) {
188+
GCPerformanceEntry* entry = static_cast<GCPerformanceEntry*>(handle->data);
189+
Environment* env = entry->env();
189190
HandleScope scope(env->isolate());
190191
Local<Context> context = env->context();
191192

@@ -203,6 +204,10 @@ void PerformanceGCCallback(Environment* env, void* ptr) {
203204
}
204205

205206
delete entry;
207+
auto closeCB = [](uv_handle_t* handle) {
208+
delete reinterpret_cast<uv_async_t*>(handle);
209+
};
210+
uv_close(reinterpret_cast<uv_handle_t*>(handle), closeCB);
206211
}
207212

208213
// Marks the start of a GC cycle
@@ -219,11 +224,16 @@ void MarkGarbageCollectionEnd(Isolate* isolate,
219224
v8::GCCallbackFlags flags,
220225
void* data) {
221226
Environment* env = static_cast<Environment*>(data);
222-
env->SetImmediate(PerformanceGCCallback,
223-
new GCPerformanceEntry(env,
224-
static_cast<PerformanceGCKind>(type),
225-
performance_last_gc_start_mark_,
226-
PERFORMANCE_NOW()));
227+
uv_async_t* async = new uv_async_t();
228+
if (uv_async_init(env->event_loop(), async, PerformanceGCCallback))
229+
return delete async;
230+
uv_unref(reinterpret_cast<uv_handle_t*>(async));
231+
async->data =
232+
new GCPerformanceEntry(env,
233+
static_cast<PerformanceGCKind>(type),
234+
performance_last_gc_start_mark_,
235+
PERFORMANCE_NOW());
236+
CHECK_EQ(0, uv_async_send(async));
227237
}
228238

229239

test/parallel/test-performance-gc.js

+10
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,13 @@ const kinds = [
5151
// Keep the event loop alive to witness the GC async callback happen.
5252
setImmediate(() => setImmediate(() => 0));
5353
}
54+
55+
// GC should not keep the event loop alive
56+
{
57+
let didCall = false;
58+
process.on('beforeExit', () => {
59+
assert(!didCall);
60+
didCall = true;
61+
global.gc();
62+
});
63+
}

0 commit comments

Comments
 (0)