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

AsyncWrap external API improvements #3461

Merged
merged 3 commits into from
Nov 6, 2015

Conversation

trevnorris
Copy link
Contributor

Few improvements to make the AsyncWrap external API more usable. These include:

  1. Hooks are optional (except the init hook).
  2. All AsyncWrap instances receive a unique id.
  3. Call callback from the AsyncWrap destructor.

R=@bnoordhuis

CI: https://ci.nodejs.org/job/node-test-pull-request/548/

@trevnorris trevnorris added the c++ Issues and PRs that require attention from people who are familiar with C++. label Oct 20, 2015
env->set_async_hooks_pre_function(args[1].As<Function>());
env->set_async_hooks_post_function(args[2].As<Function>());
if (args[0]->IsFunction())
env->set_async_hooks_init_function(args[0].As<Function>());
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Since the init callback is always necessary, will change this to fail if init callback is not passed.

@jasnell
Copy link
Member

jasnell commented Oct 21, 2015

semver-minor or major?

env->set_async_hooks_init_function(args[0].As<Function>());
env->set_async_hooks_pre_function(args[1].As<Function>());
env->set_async_hooks_post_function(args[2].As<Function>());
if (args[0]->IsFunction())
Copy link
Contributor

Choose a reason for hiding this comment

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

Earlier if the arguments are not functions, it will throw. This change will ignore the values if they are not functions. Is that okay?

Edit: Ah, I see it now. This is the first point in the PR description.

@thefourtheye
Copy link
Contributor

Why do we need the unique ids?

@rvagg
Copy link
Member

rvagg commented Oct 21, 2015

re semver—so far we've been treating AsyncWrap changes as neither major nor minor but getting all changes in to patch releases because it's sort of in "unofficial" land, undocumented anyway

at some point we have to grow out of that though! @trevnorris do you have a feel for maturity of AsyncWrap? We can't leave it in limbo, it needs to be an official API at some point.

@trevnorris
Copy link
Contributor Author

@thefourtheye For the new destructed callback. We can't return the instance like we do in the others, so instead we give the id of the instance being destroyed.

@rvagg My recent flurry of PRs regarding async wrap is to address this. I feel like it's almost ready to be made publicly experimental API, but there is still some cleanup that needs to be done. e.g. it can't simply abort if something goes wrong. like right now if any of the callbacks throw the program will simply crash. I did that b/c I honestly have no idea what will happen to the application if execution proceeds after an exception.

There's also the case of timers. Currently none of these are being tracked. While this could be delayed until after making the API public, I have a couple implementation ideas to try out first.

@trevnorris
Copy link
Contributor Author

Added test and rebased.

CI: https://ci.nodejs.org/job/node-test-pull-request/556/

@rvagg
Copy link
Member

rvagg commented Oct 25, 2015

needs someone more qualified than me to review, @nodejs/tracing, @bnoordhuis or @thlorenz perhaps?

@@ -103,6 +103,9 @@ RetainedObjectInfo* WrapperInfo(uint16_t class_id, Local<Value> wrapper) {

static void EnableHooksJS(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
Local<Function> init_fn = env->async_hooks_init_function();
if (init_fn.IsEmpty() || !init_fn->IsFunction())
Copy link
Member

Choose a reason for hiding this comment

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

why is the function check required? In the reset of the code only init_fn.IsEmpty() is used to check of async-wrap is enabled. The SetupHooks function also checks that init_fn is a function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently in AsyncWrap::AsyncWrap we check if the init hook is set. If not then it returns early. If this is the case then bits_ will never be set and no additional callback will ever be called for that object as a result. I can probably rethink the logic, but this seemed the most straight forward for this PR.

@AndreasMadsen
Copy link
Member

Very awesome with the destructor hook. But how does this fit into the tick/turn logic, does it follow immediately after the post hook in the oncomplete cases?

@trevnorris
Copy link
Contributor Author

how does this fit into the tick/turn logic, does it follow immediately after the post hook in the oncomplete cases?

Doesn't have a specific timing. Sometimes the handle is cleaned up on a close() call, and other times it's cleaned up whenever GC gets around to it. It fires whenever the class instance is being destructed, and that can be done manually or be handled automatically.

@indutny
Copy link
Member

indutny commented Nov 6, 2015

LGTM, if CI is green

@trevnorris
Copy link
Contributor Author

One final CI after rebase: https://ci.nodejs.org/job/node-test-pull-request/681/

Only enforce that the init callback is passed to setupHooks(). The
remaining hooks can be optionally passed.

Throw if async_wrap.enable() runs before setting the init callback or if
setupHooks() is called while async wrap is enabled.

Add test to verify calls throw appropriately.

PR-URL: nodejs#3461
Reviewed-By: Fedor Indutny <[email protected]>
New instances of AsyncWrap are automatically assigned a unique id. The
value will be used in future commits to communicate additional
information via the async hooks.

While the largest value we can reliably communicate to JS is 2^53, even
if a new AsyncWrap is created every 100ns the uid won't reach its end
for 28.5 years.

PR-URL: nodejs#3461
Reviewed-By: Fedor Indutny <[email protected]>
Call a user's callback to notify that the handle has been destroyed.
Only pass the id of the AsyncWrap instance since the object no longer
exists.

The object that's being destructed should never be inspected within the
callback or any time afterward.

This commit make a breaking change. The init callback will now be passed
arguments in the order of provider, id, parent.

PR-URL: nodejs#3461
Reviewed-By: Fedor Indutny <[email protected]>
@trevnorris trevnorris merged commit bb1bd76 into nodejs:master Nov 6, 2015
@trevnorris
Copy link
Contributor Author

All CI failures were Jenkins related.

Landed in a4e9487, 80a66ba and bb1bd76.

@trevnorris
Copy link
Contributor Author

@jasnell Thoughts on LTS worthy? Since this is technically internal API still, no API breakage.

@trevnorris trevnorris deleted the async-id-destruct branch November 6, 2015 23:42
trevnorris added a commit that referenced this pull request Nov 7, 2015
Only enforce that the init callback is passed to setupHooks(). The
remaining hooks can be optionally passed.

Throw if async_wrap.enable() runs before setting the init callback or if
setupHooks() is called while async wrap is enabled.

Add test to verify calls throw appropriately.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
trevnorris added a commit that referenced this pull request Nov 7, 2015
New instances of AsyncWrap are automatically assigned a unique id. The
value will be used in future commits to communicate additional
information via the async hooks.

While the largest value we can reliably communicate to JS is 2^53, even
if a new AsyncWrap is created every 100ns the uid won't reach its end
for 28.5 years.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
trevnorris added a commit that referenced this pull request Nov 7, 2015
Call a user's callback to notify that the handle has been destroyed.
Only pass the id of the AsyncWrap instance since the object no longer
exists.

The object that's being destructed should never be inspected within the
callback or any time afterward.

This commit make a breaking change. The init callback will now be passed
arguments in the order of provider, id, parent.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
This was referenced Nov 10, 2015
@MylesBorins
Copy link
Contributor

@jasnell @rvagg thoughts on lts? Don't feel qualified to make this decision

fwiw the commits all land cleanly and test / test-addons pass

@rvagg
Copy link
Member

rvagg commented Nov 30, 2015

I'm a tentative +1 on getting the asyncwrap improvements into v4.x but I think this might be worth discussing in either a CTC or LTS WG meeting because the API is getting close to being finalised and documented, how far do we go in keeping it updated in LTS? It's useful but mostly a background feature for everyone but those in the know.

@MylesBorins
Copy link
Contributor

@rvagg @trevnorris was a decision reached regarding landing this on 4.x?

@rvagg
Copy link
Member

rvagg commented Dec 15, 2015

I can't recall exactly but I'm pretty sure we agreed to land AsyncWrap functionality on to v4 but to stop short of announcing it as a major addition—we really just want to get parity where it gets to before it's announced as public in v5/v6 so we don't risk shipping a 1/2 baked version that's not compatible with anything else and is simply technical debt in v4.

@rvagg rvagg mentioned this pull request Dec 17, 2015
trevnorris added a commit that referenced this pull request Dec 29, 2015
Only enforce that the init callback is passed to setupHooks(). The
remaining hooks can be optionally passed.

Throw if async_wrap.enable() runs before setting the init callback or if
setupHooks() is called while async wrap is enabled.

Add test to verify calls throw appropriately.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
trevnorris added a commit that referenced this pull request Dec 29, 2015
New instances of AsyncWrap are automatically assigned a unique id. The
value will be used in future commits to communicate additional
information via the async hooks.

While the largest value we can reliably communicate to JS is 2^53, even
if a new AsyncWrap is created every 100ns the uid won't reach its end
for 28.5 years.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
trevnorris added a commit that referenced this pull request Dec 29, 2015
Call a user's callback to notify that the handle has been destroyed.
Only pass the id of the AsyncWrap instance since the object no longer
exists.

The object that's being destructed should never be inspected within the
callback or any time afterward.

This commit make a breaking change. The init callback will now be passed
arguments in the order of provider, id, parent.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
@MylesBorins
Copy link
Contributor

landed in v4.x-staging as ae46a23...dab862f

If there are any concerns about this being in LTS please let me know and I'll back the commits out.

@AndreasMadsen
Copy link
Member

@thealphanerd This was discussed in nodejs/Release#59 (comment)

CTC discussion suggested that AsyncWrap should be brought up to parity with the final version, just not announced as a public and supported API in v4.

MylesBorins pushed a commit that referenced this pull request Jan 19, 2016
Only enforce that the init callback is passed to setupHooks(). The
remaining hooks can be optionally passed.

Throw if async_wrap.enable() runs before setting the init callback or if
setupHooks() is called while async wrap is enabled.

Add test to verify calls throw appropriately.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
MylesBorins pushed a commit that referenced this pull request Jan 19, 2016
New instances of AsyncWrap are automatically assigned a unique id. The
value will be used in future commits to communicate additional
information via the async hooks.

While the largest value we can reliably communicate to JS is 2^53, even
if a new AsyncWrap is created every 100ns the uid won't reach its end
for 28.5 years.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
MylesBorins pushed a commit that referenced this pull request Jan 19, 2016
Call a user's callback to notify that the handle has been destroyed.
Only pass the id of the AsyncWrap instance since the object no longer
exists.

The object that's being destructed should never be inspected within the
callback or any time afterward.

This commit make a breaking change. The init callback will now be passed
arguments in the order of provider, id, parent.

PR-URL: #3461
Reviewed-By: Fedor Indutny <[email protected]>
@MylesBorins MylesBorins mentioned this pull request Jan 19, 2016
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++.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants