Skip to content

Commit 0e116a0

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. Backport-PR-URL: #18050 PR-URL: #18051 Fixes: #18047 Refs: #18020 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
1 parent 1181ff7 commit 0e116a0

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
@@ -182,8 +182,9 @@ void SetupPerformanceObservers(const FunctionCallbackInfo<Value>& args) {
182182
}
183183

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

@@ -200,6 +201,10 @@ void PerformanceGCCallback(Environment* env, void* ptr) {
200201
}
201202

202203
delete entry;
204+
auto closeCB = [](uv_handle_t* handle) {
205+
delete reinterpret_cast<uv_async_t*>(handle);
206+
};
207+
uv_close(reinterpret_cast<uv_handle_t*>(handle), closeCB);
203208
}
204209

205210
// Marks the start of a GC cycle
@@ -216,11 +221,16 @@ void MarkGarbageCollectionEnd(Isolate* isolate,
216221
v8::GCCallbackFlags flags,
217222
void* data) {
218223
Environment* env = static_cast<Environment*>(data);
219-
env->SetImmediate(PerformanceGCCallback,
220-
new GCPerformanceEntry(env,
221-
static_cast<PerformanceGCKind>(type),
222-
performance_last_gc_start_mark_,
223-
PERFORMANCE_NOW()));
224+
uv_async_t* async = new uv_async_t();
225+
if (uv_async_init(env->event_loop(), async, PerformanceGCCallback))
226+
return delete async;
227+
uv_unref(reinterpret_cast<uv_handle_t*>(async));
228+
async->data =
229+
new GCPerformanceEntry(env,
230+
static_cast<PerformanceGCKind>(type),
231+
performance_last_gc_start_mark_,
232+
PERFORMANCE_NOW());
233+
CHECK_EQ(0, uv_async_send(async));
224234
}
225235

226236

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)