Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[async_wrap] call destroy hooks on uv_timer_t #13369

Merged
merged 2 commits into from
Jun 2, 2017

Conversation

trevnorris
Copy link
Contributor

@trevnorris trevnorris commented Jun 1, 2017

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • commit message follows commit guidelines
Affected core subsystem(s)

async_wrap

Summary

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

@addaleax Hope you don't mind that I added the relevant test you included in #13286 (left you as the committer, but altered the commit message)

CI: https://ci.nodejs.org/job/node-test-commit/10272/

@nodejs-github-bot nodejs-github-bot added async_wrap c++ Issues and PRs that require attention from people who are familiar with C++. labels Jun 1, 2017
});
});

process.on('exit', () => assert.strictEqual(destroyTcpWrapCallCount, 2));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just fyi, the test from #13353 might be better because it uses AsyncResource to trigger the destroy call, so it doesn’t rely on .close() specifics.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah thanks. And I can remove the setTimeout() at the bottom of the test.

if (ret.IsEmpty()) {
ClearFatalExceptionHandlers(env);
FatalException(env->isolate(), try_catch);
do {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will this not call the destroy hook synchronously if emitDestroy was called from within the destroy hook? We don't want that because destroy hooks should never be called synchronously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndreasMadsen It would act like a nextTick(). The current execution scope would complete before the destroy hook was called.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that came to me a few hours after I wrote that. Thanks!

@mscdex mscdex added the timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout. label Jun 1, 2017
Copy link
Member

@addaleax addaleax left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thank you a lot!

@trevnorris
Copy link
Contributor Author

@addaleax Thanks for pointing to the other test. Also forgot to include your test for clearImmediate().

CI: https://ci.nodejs.org/job/node-test-commit/10290/

Copy link
Member

@AndreasMadsen AndreasMadsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks for taking care of this.

trevnorris and others added 2 commits June 2, 2017 16:21
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: nodejs#13262
PR-URL: nodejs#13369
Reviewed-By: Anna Henningsen <[email protected]>
Reviewed-By: Andreas Madsen <[email protected]>
Verify that the destroy callback for a TCP server is called before exit
if it is closed in another destroy callback.

Fixes: nodejs#13262
PR-URL: nodejs#13369
Reviewed-By: Trevor Norris <[email protected]>
Reviewed-By: Andreas Madsen <[email protected]>
@trevnorris trevnorris merged commit 78b1358 into nodejs:master Jun 2, 2017
@trevnorris
Copy link
Contributor Author

Landed in 1e44fd9 and 78b1358

Thanks all!

jasnell pushed a commit that referenced this pull request Jun 5, 2017
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]>
jasnell pushed a commit that referenced this pull request Jun 5, 2017
Verify that the destroy callback for a TCP server is called before exit
if it is closed in another destroy callback.

Fixes: #13262
PR-URL: #13369
Reviewed-By: Trevor Norris <[email protected]>
Reviewed-By: Andreas Madsen <[email protected]>
@jasnell jasnell mentioned this pull request Jun 5, 2017
@gibfahn gibfahn mentioned this pull request Jun 15, 2017
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. timers Issues and PRs related to the timers subsystem / setImmediate, setInterval, setTimeout.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Questions: async_hooks destroy callback vs setImmediate
7 participants