Skip to content

Commit 6917df2

Browse files
trevnorrisjasnell
authored andcommitted
async_wrap: run destroy in uv_timer_t
Calling the destroy callbacks in a uv_idle_t causes a timing issue where if a handle or request is closed then the class isn't deleted until uv_close() callbacks are called (which happens after the poll phase). This results in some destroy callbacks not being called just before the application exits. So instead switch the destroy callbacks to be called in a uv_timer_t with the timeout set to zero. When uv_run() is called with UV_RUN_ONCE the final operation of the event loop is to process all remaining timers. By setting the timeout to zero it results in the destroy callbacks being processed after uv_close() but before uv_run() returned. Processing the destroyed ids that were previously missed. Also, process the destroy_ids_list() in a do {} while() loop that makes sure the vector is empty before returning. Which also makes running clear() unnecessary. Fixes: #13262 PR-URL: #13369 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Andreas Madsen <[email protected]>
1 parent 406c2cd commit 6917df2

File tree

5 files changed

+29
-42
lines changed

5 files changed

+29
-42
lines changed

src/async-wrap.cc

+19-21
Original file line numberDiff line numberDiff line change
@@ -138,34 +138,32 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local<Value> wrapper) {
138138
// end RetainedAsyncInfo
139139

140140

141-
static void DestroyIdsCb(uv_idle_t* handle) {
142-
uv_idle_stop(handle);
143-
144-
Environment* env = Environment::from_destroy_ids_idle_handle(handle);
141+
static void DestroyIdsCb(uv_timer_t* handle) {
142+
Environment* env = Environment::from_destroy_ids_timer_handle(handle);
145143

146144
HandleScope handle_scope(env->isolate());
147145
Context::Scope context_scope(env->context());
148146
Local<Function> fn = env->async_hooks_destroy_function();
149147

150148
TryCatch try_catch(env->isolate());
151149

152-
std::vector<double> destroy_ids_list;
153-
destroy_ids_list.swap(*env->destroy_ids_list());
154-
for (auto current_id : destroy_ids_list) {
155-
// Want each callback to be cleaned up after itself, instead of cleaning
156-
// them all up after the while() loop completes.
157-
HandleScope scope(env->isolate());
158-
Local<Value> argv = Number::New(env->isolate(), current_id);
159-
MaybeLocal<Value> ret = fn->Call(
160-
env->context(), Undefined(env->isolate()), 1, &argv);
161-
162-
if (ret.IsEmpty()) {
163-
ClearFatalExceptionHandlers(env);
164-
FatalException(env->isolate(), try_catch);
150+
do {
151+
std::vector<double> destroy_ids_list;
152+
destroy_ids_list.swap(*env->destroy_ids_list());
153+
for (auto current_id : destroy_ids_list) {
154+
// Want each callback to be cleaned up after itself, instead of cleaning
155+
// them all up after the while() loop completes.
156+
HandleScope scope(env->isolate());
157+
Local<Value> argv = Number::New(env->isolate(), current_id);
158+
MaybeLocal<Value> ret = fn->Call(
159+
env->context(), Undefined(env->isolate()), 1, &argv);
160+
161+
if (ret.IsEmpty()) {
162+
ClearFatalExceptionHandlers(env);
163+
FatalException(env->isolate(), try_catch);
164+
}
165165
}
166-
}
167-
168-
env->destroy_ids_list()->clear();
166+
} while (!env->destroy_ids_list()->empty());
169167
}
170168

171169

@@ -174,7 +172,7 @@ static void PushBackDestroyId(Environment* env, double id) {
174172
return;
175173

176174
if (env->destroy_ids_list()->empty())
177-
uv_idle_start(env->destroy_ids_idle_handle(), DestroyIdsCb);
175+
uv_timer_start(env->destroy_ids_timer_handle(), DestroyIdsCb, 0, 0);
178176

179177
env->destroy_ids_list()->push_back(id);
180178
}

src/env-inl.h

+5-5
Original file line numberDiff line numberDiff line change
@@ -356,13 +356,13 @@ inline uv_idle_t* Environment::immediate_idle_handle() {
356356
return &immediate_idle_handle_;
357357
}
358358

359-
inline Environment* Environment::from_destroy_ids_idle_handle(
360-
uv_idle_t* handle) {
361-
return ContainerOf(&Environment::destroy_ids_idle_handle_, handle);
359+
inline Environment* Environment::from_destroy_ids_timer_handle(
360+
uv_timer_t* handle) {
361+
return ContainerOf(&Environment::destroy_ids_timer_handle_, handle);
362362
}
363363

364-
inline uv_idle_t* Environment::destroy_ids_idle_handle() {
365-
return &destroy_ids_idle_handle_;
364+
inline uv_timer_t* Environment::destroy_ids_timer_handle() {
365+
return &destroy_ids_timer_handle_;
366366
}
367367

368368
inline void Environment::RegisterHandleCleanup(uv_handle_t* handle,

src/env.cc

+1-12
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,7 @@ void Environment::Start(int argc,
4949
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_prepare_handle_));
5050
uv_unref(reinterpret_cast<uv_handle_t*>(&idle_check_handle_));
5151

52-
uv_idle_init(event_loop(), destroy_ids_idle_handle());
53-
uv_unref(reinterpret_cast<uv_handle_t*>(destroy_ids_idle_handle()));
52+
uv_timer_init(event_loop(), destroy_ids_timer_handle());
5453

5554
auto close_and_finish = [](Environment* env, uv_handle_t* handle, void* arg) {
5655
handle->data = env;
@@ -102,16 +101,6 @@ void Environment::CleanupHandles() {
102101
while (handle_cleanup_waiting_ != 0)
103102
uv_run(event_loop(), UV_RUN_ONCE);
104103

105-
// Closing the destroy_ids_idle_handle_ within the handle cleanup queue
106-
// prevents the async wrap destroy hook from being called.
107-
uv_handle_t* handle =
108-
reinterpret_cast<uv_handle_t*>(&destroy_ids_idle_handle_);
109-
handle->data = this;
110-
handle_cleanup_waiting_ = 1;
111-
uv_close(handle, [](uv_handle_t* handle) {
112-
static_cast<Environment*>(handle->data)->FinishHandleCleanup(handle);
113-
});
114-
115104
while (handle_cleanup_waiting_ != 0)
116105
uv_run(event_loop(), UV_RUN_ONCE);
117106
}

src/env.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -526,10 +526,10 @@ class Environment {
526526
inline uint32_t watched_providers() const;
527527

528528
static inline Environment* from_immediate_check_handle(uv_check_t* handle);
529-
static inline Environment* from_destroy_ids_idle_handle(uv_idle_t* handle);
529+
static inline Environment* from_destroy_ids_timer_handle(uv_timer_t* handle);
530530
inline uv_check_t* immediate_check_handle();
531531
inline uv_idle_t* immediate_idle_handle();
532-
inline uv_idle_t* destroy_ids_idle_handle();
532+
inline uv_timer_t* destroy_ids_timer_handle();
533533

534534
// Register clean-up cb to be called on environment destruction.
535535
inline void RegisterHandleCleanup(uv_handle_t* handle,
@@ -662,7 +662,7 @@ class Environment {
662662
IsolateData* const isolate_data_;
663663
uv_check_t immediate_check_handle_;
664664
uv_idle_t immediate_idle_handle_;
665-
uv_idle_t destroy_ids_idle_handle_;
665+
uv_timer_t destroy_ids_timer_handle_;
666666
uv_prepare_t idle_prepare_handle_;
667667
uv_check_t idle_check_handle_;
668668
AsyncHooks async_hooks_;

test/async-hooks/test-writewrap.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ function checkDestroyedWriteWraps(n, stage) {
5353
assert.strictEqual(typeof w.uid, 'number', 'uid is a number');
5454
assert.strictEqual(typeof w.triggerId, 'number', 'triggerId is a number');
5555

56-
checkInvocations(w, { init: 1, destroy: 1 }, 'when ' + stage);
56+
checkInvocations(w, { init: 1 }, 'when ' + stage);
5757
}
5858
as.forEach(checkValidWriteWrap);
5959
}

0 commit comments

Comments
 (0)