Skip to content

Commit 8803b69

Browse files
committed
async_wrap: schedule destroy hook as unref
Since the `DestroyAsyncIdsCallback` in Node.js can be scheduled as a result of GC, that means that it can accidentally keep the event loop open when it shouldn't. Replace `SetImmediate` with the newly introduced `SetUnrefImmediate` and in addition introduce RunBeforeExit callbacks, of which `DestroyAsyncIdsCallback` is now the first. These callbacks will run before the `beforeExit` event is emitted (which will now only be emitted if the event loop is still not active). PR-URL: #18241 Fixes: #18190 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Andreas Madsen <[email protected]>
1 parent d62566e commit 8803b69

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

src/async_wrap.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ static void DestroyAsyncIdsCallback(Environment* env, void* data) {
159159
} while (!env->destroy_async_id_list()->empty());
160160
}
161161

162+
static void DestroyAsyncIdsCallback(void* arg) {
163+
Environment* env = static_cast<Environment*>(arg);
164+
if (!env->destroy_async_id_list()->empty())
165+
DestroyAsyncIdsCallback(env, nullptr);
166+
}
167+
162168

163169
void AsyncWrap::EmitPromiseResolve(Environment* env, double async_id) {
164170
AsyncHooks* async_hooks = env->async_hooks();
@@ -502,6 +508,8 @@ void AsyncWrap::Initialize(Local<Object> target,
502508
Isolate* isolate = env->isolate();
503509
HandleScope scope(isolate);
504510

511+
env->BeforeExit(DestroyAsyncIdsCallback, env);
512+
505513
env->SetMethod(target, "setupHooks", SetupHooks);
506514
env->SetMethod(target, "pushAsyncIds", PushAsyncIds);
507515
env->SetMethod(target, "popAsyncIds", PopAsyncIds);
@@ -663,7 +671,7 @@ void AsyncWrap::EmitDestroy(Environment* env, double async_id) {
663671
return;
664672

665673
if (env->destroy_async_id_list()->empty()) {
666-
env->SetImmediate(DestroyAsyncIdsCallback, nullptr);
674+
env->SetUnrefImmediate(DestroyAsyncIdsCallback, nullptr);
667675
}
668676

669677
env->destroy_async_id_list()->push_back(async_id);

src/env.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,17 @@ void Environment::PrintSyncTrace() const {
211211
fflush(stderr);
212212
}
213213

214+
void Environment::RunBeforeExitCallbacks() {
215+
for (BeforeExitCallback before_exit : before_exit_functions_) {
216+
before_exit.cb_(before_exit.arg_);
217+
}
218+
before_exit_functions_.clear();
219+
}
220+
221+
void Environment::BeforeExit(void (*cb)(void* arg), void* arg) {
222+
before_exit_functions_.push_back(BeforeExitCallback{cb, arg});
223+
}
224+
214225
void Environment::RunAtExitCallbacks() {
215226
for (AtExitCallback at_exit : at_exit_functions_) {
216227
at_exit.cb_(at_exit.arg_);

src/env.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,8 @@ class Environment {
658658
const char* name,
659659
v8::FunctionCallback callback);
660660

661+
void BeforeExit(void (*cb)(void* arg), void* arg);
662+
void RunBeforeExitCallbacks();
661663
void AtExit(void (*cb)(void* arg), void* arg);
662664
void RunAtExitCallbacks();
663665

@@ -778,6 +780,12 @@ class Environment {
778780

779781
double* fs_stats_field_array_;
780782

783+
struct BeforeExitCallback {
784+
void (*cb_)(void* arg);
785+
void* arg_;
786+
};
787+
std::list<BeforeExitCallback> before_exit_functions_;
788+
781789
struct AtExitCallback {
782790
void (*cb_)(void* arg);
783791
void* arg_;

src/node.cc

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4319,6 +4319,14 @@ void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
43194319
}
43204320

43214321

4322+
void RunBeforeExit(Environment* env) {
4323+
env->RunBeforeExitCallbacks();
4324+
4325+
if (!uv_loop_alive(env->event_loop()))
4326+
EmitBeforeExit(env);
4327+
}
4328+
4329+
43224330
void EmitBeforeExit(Environment* env) {
43234331
HandleScope handle_scope(env->isolate());
43244332
Context::Scope context_scope(env->context());
@@ -4469,7 +4477,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
44694477
if (more)
44704478
continue;
44714479

4472-
EmitBeforeExit(&env);
4480+
RunBeforeExit(&env);
44734481

44744482
// Emit `beforeExit` if the loop became alive either after emitting
44754483
// event, or after running some callbacks.

0 commit comments

Comments
 (0)