GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
GeezerVison is showing github.com — async_hooks: use parent promise as triggerId in init hook by JiaLiPassion · Pull Request #13367 · nodejs/node Conversation
Contributor
async_hooks init callback will be triggered when promise newly created, In this commit, we use the parent promise's id as triggerId to Fixes: #13302 Thanks for @AndreasMadsen and @addaleax 's great help! Please review. Checklist
Affected core subsystem(s)
Sorry, something went wrong. Uh oh!
There was an error while loading. Please reload this page. All reactions
|
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Maybe just
+ // set parent promise's async Id as this promise's triggerId + if (parent->IsPromise()) { + // parent promise exists, current promise + // is a chained promise, so we set parent promise's id as + // current promise's triggerId + double trigger_id = Unwrap<PromiseWrap>(parent.As<Promise>())->get_id(); + env->set_init_trigger_id(trigger_id); + } wrap = new PromiseWrap(env, promise, silent);
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@AndreasMadsen , Thank very much for the review, I have modified this one.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
28d4db5 to
d721c6b
Compare
There was an error while loading. Please reload this page.
|
@AndreasMadsen , about the sample code you provided, Consider the following code: const p0 = new Promise((resolve) => resolve(1)); const p1 = p0.then(function () { // async_hooks.currentId() === asyncId(p1) p2 = p1.then(function () { // trigger_id = asyncId(p1) }); return 2; }); I make a test with this PR compiled node. const async_hooks = require('async_hooks'); function init(id, provider, parentId, parentHandle) { process._rawDebug('init: ', id, 'triggerId: ', parentId, 'currentId', async_hooks.currentId()); } function before(id) { process._rawDebug('before', id, async_hooks.currentId()); } function after(id) { process._rawDebug('after', id, async_hooks.currentId()); } function destroy(id) { process._rawDebug('destroy', id); } async_hooks.createHook({init, before, after, destroy}).enable(); debugger; const p = new Promise ( resolve => { process._rawDebug('execute p'); resolve(1);} ); const p1 = p.then(val => { process._rawDebug('p1 then', val); const p2 = p1.then((val1) => { process._rawDebug('p2 then', val1); }); return 2; }); the output is p is promise without parent, so I think this a correct result. And I tried another case setTimeout(() => { const p = new Promise(resolve => { process._rawDebug('execute p'); resolve(1); }); const p1 = p.then(val => { process._rawDebug('p1 then', val); const p2 = p1.then((val1) => { process._rawDebug('p2 then', val1); }); return 2; }); }, 100); the output is I think the result is also ok, but I don't quite understand the why Please review. Thank you very much! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Interesting, I think something is wrong with init: 2 triggerId: 1 currentId 1 execute p init: 3 triggerId: 2 currentId 1 before 3 1 p1 then 1 init: 4 triggerId: 3 currentId 1 // wrong, currentId should be 3 after 3 1 before 4 1 p2 then 2 after 4 1 PS: I very much doubt this change is responsible for the bug. /cc @nodejs/async_hooks |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen, I am not sure I understand the lifecycle of currentId, so I do not know what is the correct result,just like the another sample involve setTimeout will have a currentId 0,I do not understand why. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@JiaLiPassion The In some cases, there is no |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , Thank you for the explanation.
Yeah, this is making sense.
you mean there is only And yes, in my setTimeout example, there is |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
For example in the TCP server you will get: This is because the TCP socket is created in C++ land and then send to the callback. Since this happens before any javascript the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The code change looks good to me, aside from one edge case. I still am not 100 % sure about this being the right thing, but if Andreas says so, I trust him.
I would still really like to see a test that shows that getting the original execution scope remains possible, because that is something that actual use cases will rely on.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
You need to also check whether Unwrap<PromiseWrap>() returned nullptr (that could happen if async_hooks were enabled after parent was created, but before promise was created).
If it is, you’ll need to basically do the same thing we’re doing just below; something along the lines of:
auto parent_wrap = new PromiseWrap(env, parent, true); parent_wrap->MakeWeak(parent_wrap);
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@addaleax , thank you for the review.
So the code should look like
auto parent_wrap = Unwrap<PromiseWrap>(parent.As<Promise>());
if (parent_wrap != nullptr) {
double trigger_id = parent_wrap->get_id();
env->set_init_trigger_id(trigger_id);
}
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@JiaLiPassion I think you would still want to create a new PromiseWrap for the parent Promise. If you do that, you don’t need to check if (parent_wrap != nullptr).
(Basically, look at the existing code here and how it adds a parent_wrap when there isn’t any. If it’s unclear, feel free to ping me and I’ll explain in more detail. :))
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@addaleax , thank you so much! sorry for so many basic questions...
So the code should like
// create a new PromiseWrap for parent promise
auto parent_wrap = new PromiseWrap(env, parent, true);
parent_wrap->MakeWeak(parent_wrap);
// get id from parentWrap
double trigger_id = parent_wrap->get_id();
env->set_init_trigger_id(trigger_id);
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
The new PromiseWrap should only be created if Unwrap<> gives you a nullptr (otherwise there already is one, so we don’t need that). But yes, that’s the general idea.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@addaleax , thank you, so it will look like
if (parent->IsPromise()) {
auto parent_wrap = Unwrap<PromiseWrap>(parent.As<Promise>());
if (parent_wrap == nullptr) {
// create a new PromiseWrap for parent promise with silent parameter
parent_wrap = new PromiseWrap(env, parent, true);
parent_wrap->MakeWeak(parent_wrap);
}
// get id from parentWrap
double trigger_id = parent_wrap->get_id();
env->set_init_trigger_id(trigger_id);
}
}
I just updated the code, ping @addaleax !
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Sorry to burst your bubble, but the Also since this doesn't solve the problem that @JiaLiPassion originally asked, regarding detecting if a parentPromise exists, I would like to explore some alternatives. tl;dr; I think this makes sense, but I'm generally uncertain about
Yeah, we need to fix the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen Regarding
Writing this comment, it still seems to me like we should maybe just expose the parent Promises async id as an extra argument to the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@addaleax It is the first one, but there is a thin line between causality and parent resource, which is what causes my confusion. From the documentation:
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
So if expose the parent Promise async id as an extra argument, it can resolve my original issue, that if the argument exists, the the promise is a chained one, is that correct?
do you mean the case like this? async function test() { await new Promise(resolve => resolve(1)); } process._rawDebug('before', async_hooks.currentId()); test(); process._rawDebug('after', async_hooks.currentId()); |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen Okay, makes sense.
@JiaLiPassion Yes, I’d say so.
More like this: async function test() { process._rawDebug('before', async_hooks.currentId()); await new Promise(resolve => resolve(1)); process._rawDebug('after', async_hooks.currentId()); } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
I think extra arguments to |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
That works for me; but it means that we’d have to switch the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
I think that would be the best. It is also what we discussed in the original Promise PR and what @Fishrock123 advocated for. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , sorry I just lost...
Do you mean use |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
Sort of, but we definitely shouldn't go through JS for that. For the purpose of a resource = { promise // reference to promise, parentPromise // reference to parentPromise or undefined } |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , can I just change the how to create a new Local<Object> promise_resource = Object::New(env->isolate()); FORCE_SET_TARGET_FIELD(promise_resource, "promise", promise); FORCE_SET_TARGET_FIELD(promise_resource, "parentPromise", parent_promise); wrap = new PromiseWrap(env, promise_resource, silent); |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@JiaLiPassion I don't think that is enough. This is as far as my V8-C++ knowledge goes, I'm not sure how to tie in the GC of |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@hayes Thanks for the explanation. I misunderstood what you meant by Plese create an issue, but don't do it around the idea the
I'm pretty sure it does. But we can discuss that in the issue. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
Please keep this issue on track, this PR is just about fixing
|
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , I have updated the test cases and assert message. please review. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@JiaLiPassion Yes, that is how you’d check whether a parent promise exists. (Now that you mention it: Maybe |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@addaleax , got it , thank you very much!!! Would you please make another PR to implement it? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
@JiaLiPassion I’ll wait until you land this PR, then I can do that. :) |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| const initHooks = require('./init-hooks'); | ||
| const { checkInvocations } = require('./hook-checks'); | ||
|
|
||
| const p = (new Promise(common.mustCall(executor))); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
You don’t need the outer parentheses here
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
yes, I will remove it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| function afterresolution(val) { | ||
| assert.strictEqual(val, 5); | ||
| return val; | ||
| } |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
I think this would be a bit more readable if you just inlined executor and afterresolution
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
yes, you are right, I will change it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| } | ||
|
|
||
| process.on('exit', onexit); | ||
| function onexit() { |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
same for onexit, using process.on('exit', () => { works just fine
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
yes, I will change it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| function executor(resolve, reject) { | ||
| const as = hooks.activitiesOfTypes('PROMISE'); | ||
| assert.strictEqual(as.length, 1, 'one activities'); | ||
| assert.strictEqual(as.length, 1, 'one activity'); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
If you want to avoid merge conflicts with #13243, you can just drop this change
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
@addaleax , ok , so I just revert it.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
| assert.strictEqual(typeof a1.uid, 'number', 'uid is a number'); | ||
| assert.strictEqual(a1.triggerId, 1, 'parent uid 1'); | ||
| assert.strictEqual(a1.triggerId, a0.uid, | ||
| 'child promise should use parent uid as triggerId'); |
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I would just not use an error message here (i.e. assert.strictEqual(a1.triggerId, a0.uid);). Sorry the tests are written this way.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
ok, so I just modify it to
assert.strictEqual(a1.triggerId, a0.uid);
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
Left a few style nits. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
I will fix these, there are some other changes I would like. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , ok, thank you! so I don't need to update the source which were reviewed by @addaleax ? |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
ce12ab1 to
b8f8f4b
Compare
|
@JiaLiPassion yep, I fixed it for you :) I didn't really understand why the |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
|
@AndreasMadsen , thank you very much!!! you and @addaleax give me great help!!! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
The reason will be displayed to describe this comment to others. Learn more.
LGTM. async_hooks it a beast, so I'm happy to see that made it. I look forward to future interactions with zone.js.
Sorry, something went wrong.
There was an error while loading. Please reload this page.
There was an error while loading. Please reload this page.
|
Landed in 8741e3c 🎉 @JiaLiPassion I believe this is your first commit to core, if so, welcome on board! Thank you for the work you have been putting into this, and I think I speak for everyone in this thread if I say that we’d love to see more of that. If you’re interested, we can probably add you to the @nodejs/async_hooks review team that gets @mentioned on issues and PRs related to @jasnell I’ve left you off as a reviewer because the PR has undergone quite some change since you reviewed, and wasn’t really ready at that point anyway. I hope that’s okay. |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
async_hooks init callback will be triggered when promise newly created, in previous version, the parent promise which pass from chrome V8 PromiseHook is ignored, so we can't tell the promise is a pure new promise or a chained promise. In this commit, we use the parent promise's id as triggerId to trigger the init callback. Fixes: #13302 PR-URL: #13367 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
|
@addaleax , Thank you so much for all the great help, I am so glad to join here and definitely want to do more in the future.
Yes!!! I am very happy to join here and it will be my honor to join @nodejs/async_hooks review team to do more contributions and want to learn more from here. Thank you and @AndreasMadsen again for all the great help!!! |
Sorry, something went wrong.
There was an error while loading. Please reload this page.
async_hooks init callback will be triggered when promise newly created, in previous version, the parent promise which pass from chrome V8 PromiseHook is ignored, so we can't tell the promise is a pure new promise or a chained promise. In this commit, we use the parent promise's id as triggerId to trigger the init callback. Fixes: #13302 PR-URL: #13367 Reviewed-By: Andreas Madsen <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
* **Async Hooks**
* When one `Promise` leads to the creation of a new `Promise`, the parent
`Promise` will be identified as the trigger
[[`135f4e6643`](nodejs@135f4e6643)]
[nodejs#13367](nodejs#13367).
* **Dependencies**
* libuv has been updated to 1.12.0
[[`968596ec77`](nodejs@968596ec77)]
[nodejs#13306](nodejs#13306).
* npm has been updated to 5.0.3
[[`ffa7debd7a`](nodejs@ffa7debd7a)]
[nodejs#13487](nodejs#13487).
* **File system**
* The `fs.exists()` function now works correctly with `util.promisify()`
[[`6e0eccd7a1`](nodejs@6e0eccd7a1)]
[nodejs#13316](nodejs#13316).
* fs.Stats times are now also available as numbers
[[`c756efb25a`](nodejs@c756efb25a)]
[nodejs#13173](nodejs#13173).
* **Inspector**
* It is now possible to bind to a random port using `--inspect=0`
[[`cc6ec2fb27`](nodejs@cc6ec2fb27)]
[nodejs#5025](nodejs#5025).
* **Zlib**
* A regression in the Zlib module that made it impossible to properly
subclasses `zlib.Deflate` and other Zlib classes has been fixed.
[[`6aeb555cc4`](nodejs@6aeb555cc4)]
[nodejs#13374](nodejs#13374).
* **Async Hooks**
* When one `Promise` leads to the creation of a new `Promise`, the parent
`Promise` will be identified as the trigger
[[`135f4e6643`](135f4e6643)]
[#13367](#13367).
* **Dependencies**
* libuv has been updated to 1.12.0
[[`968596ec77`](968596ec77)]
[#13306](#13306).
* npm has been updated to 5.0.3
[[`ffa7debd7a`](ffa7debd7a)]
[#13487](#13487).
* **File system**
* The `fs.exists()` function now works correctly with `util.promisify()`
[[`6e0eccd7a1`](6e0eccd7a1)]
[#13316](#13316).
* fs.Stats times are now also available as numbers
[[`c756efb25a`](c756efb25a)]
[#13173](#13173).
* **Inspector**
* It is now possible to bind to a random port using `--inspect=0`
[[`cc6ec2fb27`](cc6ec2fb27)]
[#5025](#5025).
* **Zlib**
* A regression in the Zlib module that made it impossible to properly
subclasses `zlib.Deflate` and other Zlib classes has been fixed.
[[`6aeb555cc4`](6aeb555cc4)]
[#13374](#13374).
* **Async Hooks**
* When one `Promise` leads to the creation of a new `Promise`, the parent
`Promise` will be identified as the trigger
[[`135f4e6643`](135f4e6643)]
[#13367](#13367).
* **Dependencies**
* libuv has been updated to 1.12.0
[[`968596ec77`](968596ec77)]
[#13306](#13306).
* npm has been updated to 5.0.3
[[`ffa7debd7a`](ffa7debd7a)]
[#13487](#13487).
* **File system**
* The `fs.exists()` function now works correctly with `util.promisify()`
[[`6e0eccd7a1`](6e0eccd7a1)]
[#13316](#13316).
* fs.Stats times are now also available as numbers
[[`c756efb25a`](c756efb25a)]
[#13173](#13173).
* **Inspector**
* It is now possible to bind to a random port using `--inspect=0`
[[`cc6ec2fb27`](cc6ec2fb27)]
[#5025](#5025).
* **Zlib**
* A regression in the Zlib module that made it impossible to properly
subclasses `zlib.Deflate` and other Zlib classes has been fixed.
[[`6aeb555cc4`](6aeb555cc4)]
[#13374](#13374).
There was an error while loading. Please reload this page.
Successfully merging this pull request may close these issues.
[Feature Request]: want to get parent promise from PromiseHookInit through async_hooks
There was an error while loading. Please reload this page.