feat(async_hooks/timers): experimental support async_hooks module and timers module improvements#947
Conversation
|
This might be better as native C in quickjs-ng. The guys are pretty cool so I would check with them. |
If Quickjs exposes hook points for its internal implementation and allows user functions to be attached to them, I think it will be possible to synchronize the firing timing of all implementations that use them (including async_hooks). |
db5dbea to
56a2324
Compare
|
Can you guys check if quickjs-ng/quickjs#1033 works for what you want to do? |
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
This comment was marked as off-topic.
|
I can't generate those bindings locally (missing header files) but you can by running |
56a2324 to
a976eb9
Compare
|
@bnoordhuis I was able to set hooks into the engine from Rust and it started working, but it seemed a bit odd. reproduction.jsconst async_hooks = require('async_hooks');
const hook = async_hooks.createHook({
init(asyncId, type, triggerAsyncId) {
console.log(`init: asyncId=${asyncId}, type=${type}, triggerAsyncId=${triggerAsyncId}`);
},
before(asyncId) {
console.log(`before: asyncId=${asyncId}`);
},
after(asyncId) {
console.log(`after: asyncId=${asyncId}`);
},
promiseResolve(asyncId) {
console.log(`promiseResolve: asyncId=${asyncId}`);
},
}).enable();
Promise.resolve().then(() => {
console.log("Promise resolved1");
});NOTE:Don't worry about the id being returned as zero. That's how it's implemented for now. I don't think the PromiseHookType judgment is incorrect, but I'm getting a strange symptom where init is called three times. And promiseResolve is also displayed a lot. @richarddavison @Sytten Is there anything you noticed? |
If you're loading that script as ESM, then the module loader itself also creates two promises. See the upstream tests I added to api-test.c, they also expect at least three promises per test. |
Thanks! By referring to api-test.c, we were also able to confirm cases where before/after events occurred. new Promise(resolve => resolve({then(resolve){ resolve() }}))Please let me know if there is anything else I should check. :) |
|
Does it cover all that you need to implement async_hooks? Node has fairly extensive coverage of the API surface in its test/parallel/test-async-hooks-* tests. You may want to try and port them over. |
@bnoordhuis Thank you for your comment. I'm sorry if my intention was unclear. I was just asking what we should have checked in order to have your PRs for rquickjs and quickjs-ng merged. Additionally, we still need to review the provided Node.js test cases. |
|
Just FYI there should be a feature on all crates that use async hooks like timers. |
Yes, you're right. The hooking implementation other than Promise is not yet stable, so I was using the Timer part for testing. Now that we have prepared a helper library, will this make it easier to apply to other asynchronous processes? However, I think there is a need for discussion about where to call hooks for all asynchronous processing, so I would like to separate this into a separate PR. :) |
|
Probably yeah, but we should make it opt-in for all crates that need to be modified for async hooks. I will need to read more about them, I am not sure I fully get why we need to do calls on the rust side. |
Currently, the function In addition, I think it would be possible to implement explicit opt-in by preparing a feature flag in a crate that contains asynchronous processing.
The only native hook supported by v8 is Promise (Promise Hook API). Native hooks other than Promise are not provided in v8, so I believe that hooking events for other asynchronous processing are implemented in the NodeJS core. I haven't looked into the nodejs implementation thoroughly, but since v8 doesn't support native hooks other than Promise, this seems to be the only way to implement it. This specification is the same for quickjs-ng. |
This comment was marked as resolved.
This comment was marked as resolved.
186778a to
b064437
Compare
b064437 to
7e7dc07
Compare
|
@nabetti1720 The API is now merged in rquickjs but a regression has been introduced by the introduction of JS_TAG_SMALL_BIG_INT. As soon as this PR is merged and rquickjs is updated we should revisit this one :) |
2d87533 to
c70280b
Compare
c70280b to
0707770
Compare
5a6d241 to
1787b19
Compare
|
@richarddavison I would appreciate it if you could take a look at it when you have time. Because rebasing is starting to become difficult. :) |
richarddavison
left a comment
There was a problem hiding this comment.
Some minor changes and then I'm happy to merge, thank you!!! :)
Issue # (if available)
Fixed: #965
Related: #969
Description of changes
Experimental support for async_hooks compatible with nodejs.
It was created with the following module use cases in mind:
Experimental support
async_hooks:PROMISE,GETADDRINFOREQWRAP,Microtask,Immeditate,IntervalandTimeout. The timing of firing events other than PROMISE is implemented independently by LLRT.timersmodule improvements :queueMicrotask()has been reimplemented on the LLRT side to support asynchronous hooks. Internally, it simply registers the microtask in the queue, which is the same as the implementation method in quickjs-ng. However, before/after hooks cannot currently be detected.setImmediate()registered a task in a microtask queue, but we were replaced it with an implementation equivalent tosetTimeout(fn, -600*1000). In other words, it will be treated as having been completed beforesetTimeout(fn, 0). Combined with the PR fix: Timer execution order #969, this will bring the execution order of asynchronous timer operations into full agreement with other runtimes.The current implementation allows you to execute code like this:
async_hook_test.js
Checklist
tests/unitand/or in Rust for my feature if neededmake fixto format JS and apply Clippy auto fixesmake checktypes/directoryBy submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.