-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
Microtask queue is not flushed when promises are fulfilled in C++ #5691
Comments
It seems that calling |
One possible solution: add v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
node::CallbackScope callbackScope(isolate);
v8::Local<v8::Promise::Resolver> local = v8::Local<v8::Promise::Resolver>::New(isolate, persistent);
local->Resolve(v8::Undefined(isolate));
// resolve more promises, call functions, whatever When |
Another possible solution suggested by @ranisalt: local->Resolve(v8::Undefined(isolate));
//give it a kick
Isolate::GetCurrent()->RunMicrotasks(); But it still doesn't clear - should it be our responsibility to add |
Just running microtasks is not enough, you need to run next tick queue as well. |
@IdeaHunter I couldn't get it to work on a more complex example, I guess promise resolving/rejecting is not considered a microtask. How's a simple way to get the current environment? I searched through code but couldn't find it. In fact I'm only getting a forward declaration of |
@vkurchatkin would you mind clarifying how things run in particular and why running the microtask queue is not enough and what "kicks" the continuation? When I checked comparable code in blink I didn't find anything similar required (again, I'm far far far from an experts in these parts). |
@benjamingr the proper way to call JS from C++ is to use |
@vkurchatkin and just to make sure I understand: We never actually have any callbacks here because everything is in C++ code, we're just returning a promise and I'm pondering about the possibility of asking V8 for something akin to bluebird's |
@benjamingr correct. It also seems that it is implied by v8 authors that you should run microtasks manually when you work with promises from C++.
@chrisdickinson is working on something like this. It won't solve this problem, though. Users still need to flush scheduled callbacks explicitly at some point. |
Basically what we'd like (optimally) is for If we can't get there I agree it needs to be documented very clearly at least. |
I'm not 100% sure, I expected that V8 does that, but seems like it does not. Running microtasks each would be suboptimal and can be confusing, because promise handlers are expected to be executed asynchronously and they will be synchronous in this case. |
I meant as with a trampoline - as in "set a boolean flag to true and later if it's true run the handlers" and keep running them until none are left. There is no reason to assume they'd do it like that since the API in C++ is lower level. I just assumed it since that's what promise libraries do. |
@benjamingr the problem is that there is no definite "later" in this case |
Proof of concept: diff --git a/src/node.cc b/src/node.cc
index c0e8dbc..c713509 100644
--- a/src/node.cc
+++ b/src/node.cc
@@ -1287,6 +1287,14 @@ Local<Value> MakeCallback(Isolate* isolate,
MakeCallback(env, recv.As<Value>(), callback, argc, argv)));
}
+CallbackScope::CallbackScope(Isolate* isolate): isolate_(isolate) {
+}
+
+CallbackScope::~CallbackScope() {
+ Environment* env = Environment::GetCurrent(isolate_);
+ Environment::AsyncCallbackScope callback_scope(env);
+ env->KickNextTick(&callback_scope);
+}
enum encoding ParseEncoding(const char* encoding,
enum encoding default_encoding) {
diff --git a/src/node.h b/src/node.h
index fcbb450..2ab3768 100644
--- a/src/node.h
+++ b/src/node.h
@@ -41,6 +41,7 @@
#include "v8.h" // NOLINT(build/include_order)
#include "node_version.h" // NODE_MODULE_VERSION
+#include "util.h"
#define NODE_MAKE_VERSION(major, minor, patch) \
((major) * 0x1000 + (minor) * 0x100 + (patch))
@@ -150,6 +151,17 @@ NODE_EXTERN v8::Local<v8::Value> MakeCallback(
int argc,
v8::Local<v8::Value>* argv);
+class NODE_EXTERN CallbackScope {
+ public:
+ explicit CallbackScope(v8::Isolate* isolate);
+ ~CallbackScope();
+
+ private:
+ v8::Isolate* isolate_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackScope);
+};
+
} // namespace node
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS Then in addon: void callback(uv_work_t* req, int i) {
v8::Isolate* isolate = v8::Isolate::GetCurrent();
v8::HandleScope scope(isolate);
node::CallbackScope callback_scope(isolate);
v8::Local<v8::Promise::Resolver> local = v8::Local<v8::Promise::Resolver>::New(isolate, persistent);
local->Resolve(v8::Undefined(isolate));
} We can probably combine |
I like the RAII here, the only issue is that it's always coupled with It might as well be a ResolvePromise(local, v8::Undefined(isolate)) and save me the extra line - no? |
This works only for single promise,
You can set up scope conditionally, but that seems to be rarely useful. |
@vkurchatkin any update on this? |
All credit to Vladimir Kurchatkin, who created the original patch in nodejs#5691 (comment).
Should this remain open? |
@Trott I believe so - waiting for an update from @vkurchatkin |
I think this has not been fixed, and my guess would be that it’ll never really be fixed. I’m not sure whether it was around when this issue was opened, but maybe |
We should expose a proper way to flush microtask and nextTick queues for old API as well as N-API. |
I think #14697 would be a valid fix for this, added a regression test for it in a4505910d29597ff8fd9f95d4effcbf3196f63ca |
With `CallbackScope`, this has become possible to do properly. Fixes: nodejs#5691 PR-URL: nodejs#14697 Reviewed-By: James M Snell <jasnell@gmail.com>
With `CallbackScope`, this has become possible to do properly. Fixes: nodejs#5691 PR-URL: nodejs#14697 Reviewed-By: James M Snell <jasnell@gmail.com>
With `CallbackScope`, this has become possible to do properly. Fixes: nodejs/node#5691 PR-URL: nodejs/node#14697 Reviewed-By: James M Snell <jasnell@gmail.com>
With `CallbackScope`, this has become possible to do properly. Fixes: nodejs/node#5691 PR-URL: nodejs/node#14697 Reviewed-By: James M Snell <jasnell@gmail.com>
Reproduction:
done
should be printed after 1 seconds, but is printed after 5 seconds instead.The text was updated successfully, but these errors were encountered: