Skip to content

Commit 002937f

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).
1 parent bfe41fe commit 002937f

File tree

4 files changed

+37
-2
lines changed

4 files changed

+37
-2
lines changed

src/async_wrap.cc

+9-1
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

+11
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

+8
Original file line numberDiff line numberDiff line change
@@ -653,6 +653,8 @@ class Environment {
653653
const char* name,
654654
v8::FunctionCallback callback);
655655

656+
void BeforeExit(void (*cb)(void* arg), void* arg);
657+
void RunBeforeExitCallbacks();
656658
void AtExit(void (*cb)(void* arg), void* arg);
657659
void RunAtExitCallbacks();
658660

@@ -773,6 +775,12 @@ class Environment {
773775

774776
double* fs_stats_field_array_;
775777

778+
struct BeforeExitCallback {
779+
void (*cb_)(void* arg);
780+
void* arg_;
781+
};
782+
std::list<BeforeExitCallback> before_exit_functions_;
783+
776784
struct AtExitCallback {
777785
void (*cb_)(void* arg);
778786
void* arg_;

src/node.cc

+9-1
Original file line numberDiff line numberDiff line change
@@ -4317,6 +4317,14 @@ void AtExit(Environment* env, void (*cb)(void* arg), void* arg) {
43174317
}
43184318

43194319

4320+
void RunBeforeExit(Environment* env) {
4321+
env->RunBeforeExitCallbacks();
4322+
4323+
if (!uv_loop_alive(env->event_loop()))
4324+
EmitBeforeExit(env);
4325+
}
4326+
4327+
43204328
void EmitBeforeExit(Environment* env) {
43214329
HandleScope handle_scope(env->isolate());
43224330
Context::Scope context_scope(env->context());
@@ -4467,7 +4475,7 @@ inline int Start(Isolate* isolate, IsolateData* isolate_data,
44674475
if (more)
44684476
continue;
44694477

4470-
EmitBeforeExit(&env);
4478+
RunBeforeExit(&env);
44714479

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

0 commit comments

Comments
 (0)