FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

async_hooks: use resource objects for Promises by addaleax · Pull Request #13452 · nodejs/node · GitHub

/ node Public

async_hooks: use resource objects for Promises - #13452

Closed
addaleax wants to merge 5 commits into
nodejs:masterfrom
addaleax:promise-wrap-resource
Closed

async_hooks: use resource objects for Promises#13452
addaleax wants to merge 5 commits into
nodejs:masterfrom
addaleax:promise-wrap-resource

Conversation

addaleax commented Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member

As discussed in #13367. /cc @nodejs/async_hooks

Use PromiseWrap resource objects whose lifetimes are tied to
the Promise instances themselves to track promises, and have
a .promise getter that points to the Promise and a .parent
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

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

async_hooks

nodejs-github-bot added async_wrap c++ Issues and PRs that require attention from people who are familiar with C++. labels Jun 4, 2017
addaleax added async_hooks Issues and PRs related to the async hooks subsystem. promises Issues and PRs related to ECMAScript promises. labels Jun 4, 2017

AndreasMadsen commented Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member

Thanks for looking at this.

Do we not risk getting a very long chain of PromiseWrap objects that prevents all the promises in the chain from being easily garbage collected?

I was thinking it might be better to use the asyncId of the parent, rather than the PromiseWrap itself. If the user want to walk the .parent chain they can just store the information in a Map.

There is also the problem of resource.parent.parent being misleading if the PromiseHooks were enabled late. By returning the parent id it will always be correct and if the user enables PromiseHooks late they just don't have the information to walk the .parent chain, which is a reasonable consequence.

/cc @JiaLiPassion

AndreasMadsen commented Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member

Also, cc @Fishrock123 who was the first to request this.

JiaLiPassion commented Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Contributor

@addaleax , thank you for implementing this.

@AndreasMadsen , are you suggestion to pass the asyncId of the parent promise in init hook like below?

const parent_promise = new Promise((resolve, reject) => {resolve(5);}); 
const promise = parent_promise.then((val) => {return val;});

function init(id, type, triggerId, handle) {
    process._rawDebug(`id: ${id}, type: ${type}, triggerId: ${triggerId}, 
                                        handle.promise: ${handle.promise}, 
                                        handle.parentid: ${handle.parentId}`);
}

the output will look like

id: 2, type: PROMISE, triggerId: 1, handle.promise: {status: 'pending'}, handle.parentid: undefined
id: 3, type: PROMISE, triggerId: 2, handle.promise: {status: 'pending'}, handle.parentid: 2

And if application want the promise object, they can keep a asyncId and promise map
in the application memory themselves.
Is my understanding correct?

Copy link
Copy Markdown
Member

Is my understanding correct?

Yes. That is exactly what I mean.

Copy link
Copy Markdown
Contributor

@AndreasMadsen , got it! I still try to understand the C++ code,
If this can make GC easier to control, I agree this is a less-risky solution.

addaleax commented Jun 4, 2017

Copy link
Copy Markdown
Member Author

@AndreasMadsen @JiaLiPassion Updated!

I still try to understand the C++ code,

If you have any questions, please ask them, here or in the diff – whatever you want to know, a lot of other people will also like to know.

Copy link
Copy Markdown
Contributor

If you have any questions, please ask them, here or in the diff – whatever you want to know, a lot of other people will also like to know.

@addaleax , got it!

Comment thread src/async-wrap.cc Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

What is the cost of this? If there is any I don't think we should add it. The result is given as the first parameter in init anyway and we generally don't document that getAsyncId exists.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I thought it’s nice to have for consistency, but removing it is trivial. I’ve removed it for now, we can always revisit if necessary.

I haven’t measured, but I can’t really imagine it’s costly – it adds a method to the prototype, which should be practically free.

Comment thread src/async-wrap.cc Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

My V8-C++ knowledge might be lacking, but why do we check that resource_object_value is an Object and then cast it to an Object.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

.As<Object>() is not a cast in the JS sense of the word; you can read it as telling the engine “I’ve made sure that this is an object, now can I please use it as one?” (i.e. calling handle.As<T>() is undefined behaviour if handle is not actually a T)

AndreasMadsen Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Ah, similar to reinterpret_cast.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

In addition a Debug build it asserts, so instead of simply crashing b/c it was cast to the wrong type it'll actually say you've screwed up.

AndreasMadsen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Code LGTM. Since zone.js will be dependent on this, I think should document the PromiseWrap resource object.

addaleax commented Jun 4, 2017

Copy link
Copy Markdown
Member Author

@AndreasMadsen I’ve added some documentation… PTAL

Comment thread doc/api/async_hooks.md Outdated

AndreasMadsen Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Maybe we should add PROMISE to the provider list in async_wrap.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I’ve kept it out of it because it doesn’t quite qualify as “provided by the built-in Node.js modules”, but if you think it’s better, I can just drop this sentence and reword the existing text a bit

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

No just leave it out for now. @trevnorris suggested that maybe we should add all types to the Providers list. See #13287 (comment)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Hm. Seems we don't even include Timer or TickObject in that list. Or even as a side comment.

Anyway, only argument I'd have for adding it to the above list is because it shows up in process.binding('async_wrap').Providers. But that's not a strong argument. I'll leave this up to you.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

I think we should delete the list. The user shouldn't really on a defined list of types because the Embedder API allows for extra types.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@AndreasMadsen The reason I decided to pass the "type" (history note: using Provider was not the name I wanted to use, but switched long ago b/c of pressure to change on the PR) was for to allow filtering resources. I'd like to have a list of all "core types" (which would include TickObject and Timer) and I'd expect module authors to document their own "types".

Though because this relies a bit on internals could we put a note like "subject to change in future major (or minor?) releases without deprecation"?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

We already have a note saying resource objects are not to be trusted:

... The API for getting this information is currently not considered public ...

I guess we can be more specific.

Comment thread src/async-wrap.cc Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@addaleax , sorry for very basic question, why we add

MakeWeak(this);

in this PR, since now we only keep the asyncId of parent promise here, is there any GC issue if we don't call MakeWeak ?

addaleax Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

This question is not basic – it’s something I didn’t quite figure out until a couple days ago myself. And I’m not sure whether you’ve seen it, but the current MakeWeak call is being removed here, too: https://github.com/nodejs/node/pull/13452/files#diff-5e552c79e1538215f1621d1774852e71L315

The thing is, MakeWeak is a not the best choice of name. What it actually does is to set the 0th internal field of the associated object to the passed pointer, and then mark the handled contained in the Wrap object as weak and set a destroy callback for V8 to call.

And it needs to then make handle weak, because otherwise all V8 knows is that there is a persistent handle to the object; it doesn’t know how we use it, so it can’t garbage collect it. By making it weak, we tell V8 to destroy it once there are no other (non-weak) handles left, and to run our registered destroy callback (which will delete the C++ instance of PromiseWrap that we are using).

I agree, it’s all a bit tricky to see through. I’m mostly moving it here because that helps with avoiding duplication, and because that’s how we use it elsewhere in the code as well.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@addaleax , thank you for the explanation. I just read MakeWeak API and node_object_wrap.h, and I will continue to learn about it.

And I am not quite understand is why we don't need the MakeWeak call before this version?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

And I am not quite understand is why we don't need the MakeWeak call before this version?

It was there, it was just called after the constructor instead of being a part of it: https://github.com/nodejs/node/pull/13452/files#diff-5e552c79e1538215f1621d1774852e71L315

JiaLiPassion Jun 4, 2017
edited
Loading

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@addaleax , oh, yes, it was there, thanks. Currently I have no further questions!

trevnorris Jun 6, 2017
edited
Loading

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@addaleax

The thing is, MakeWeak is a not the best choice of name. What it actually does is to set the 0th internal field of the associated object to the passed pointer, and then mark the handled contained in the Wrap object as weak and set a destroy callback for V8 to call.

And... I'm to blame for this one. In fe2df3b I carefully went through to make sure Wrap() and ClearWrap() were called for all applicable instances forgetting that 3.5 years ago for whatever forsaken reason when I implemented BaseObject (d120d92) I made MakeWeak() automatically Wrap() the object. This was a horrible mistake on my part

Basically I would be happy if we removed the call to Wrap() in BaseObject::MakeWeak().

@JiaLiPassion

I just read MakeWeak API and node_object_wrap.h, and I will continue to learn about it.

Be careful. AsyncWrap doesn't use node_object_wrap. It uses base-object

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

@trevnorris ,

Be careful. AsyncWrap doesn't use node_object_wrap. It uses base-object

thank you for pointing out this one.

AndreasMadsen added the semver-minor PRs that contain new features and should be released in the next minor version. label Jun 7, 2017

AndreasMadsen left a comment
edited
Loading

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

LGTM.

I set it to semver-minor since this is an API change, although technically we make less than experimental guarantees about the resource object, so an argument could be made for semver-patch.

I think we should remove the type list. The user shouldn't really on a defined list of types because the Embedder API allows for extra types. However, it is not related to the PR.

refack commented Jun 7, 2017

Copy link
Copy Markdown
Contributor

I set it to semver-minor since this is an API change, although technically we make less than experimental guarantees about the resource object, so an argument could be made for semver-patch.

I think we should remove the type list. The user shouldn't really on a defined list of types because the Embedder API allows for extra types. However, it is not related to the PR.

👍

(@AndreasMadsen If you put comments in the "review summary" I can't 👍 them)

AndreasMadsen commented Jun 7, 2017
edited
Loading

Copy link
Copy Markdown
Member

(@AndreasMadsen If you put comments in the "review summary" I can't 👍 them)

I know. I prefer it this way :D evil laughter

addaleax commented Jun 7, 2017

Copy link
Copy Markdown
Member Author

Comment thread src/async-wrap.cc Outdated

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

nit: this is only used in the if statement below so mind just making it:

Local<Object> resource_object = resource_object_value.As<Object>();

addaleax added 5 commits June 8, 2017 20:34
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.
addaleax force-pushed the promise-wrap-resource branch from ec1c0d4 to 4acaed3 Compare June 8, 2017 18:36

addaleax commented Jun 8, 2017

Copy link
Copy Markdown
Member Author

trevnorris left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Thank you much

addaleax commented Jun 8, 2017

Copy link
Copy Markdown
Member Author

Landed in 8f39881

addaleax closed this Jun 8, 2017
addaleax deleted the promise-wrap-resource branch June 8, 2017 22:01
addaleax added a commit that referenced this pull request Jun 8, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
addaleax added a commit to addaleax/node that referenced this pull request Jun 10, 2017
addaleax mentioned this pull request Jun 10, 2017
jasnell pushed a commit that referenced this pull request Jun 13, 2017
PR-URL: #13561
Ref: #13452
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax added a commit that referenced this pull request Jun 17, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
addaleax added a commit that referenced this pull request Jun 17, 2017
PR-URL: #13561
Ref: #13452
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax mentioned this pull request Jun 17, 2017
addaleax added a commit that referenced this pull request Jun 21, 2017
PR-URL: #13561
Ref: #13452
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
addaleax added a commit that referenced this pull request Jun 21, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
addaleax added a commit that referenced this pull request Jun 24, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
rvagg pushed a commit that referenced this pull request Jun 29, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
addaleax added a commit that referenced this pull request Jul 11, 2017
Use `PromiseWrap` resource objects whose lifetimes are tied to
the `Promise` instances themselves to track promises, and have
a `.promise` getter that points to the `Promise` and a `.parent`
property that points to the parent Promise’s resource object,
if there is any.

The properties are implemented as getters for internal fields
rather than normal properties in the hope that it helps keep
performance for the common case that async_hooks users will
often not inspect them.

PR-URL: #13452
Reviewed-By: Andreas Madsen <amwebdk@gmail.com>
Reviewed-By: Trevor Norris <trev.norris@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

async_hooks Issues and PRs related to the async hooks subsystem. c++ Issues and PRs that require attention from people who are familiar with C++. promises Issues and PRs related to ECMAScript promises. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants


Back | FazBrowse Home | New Git URL