-
Notifications
You must be signed in to change notification settings - Fork 339
deps: fixing microtask behavior with multiple contexts #539
Conversation
I'll add some tests for this in a bit, running the existing tests locally to make sure I didn't break anything obvious there. |
CI: https://ci.nodejs.org/job/chakracore-test-pull-request/259/ Turns out that one of node's upstream tests had been hit by this bug causing it not to run, and now that it does run it turns out it fails due to an error in upstream chakracore. |
|
||
void IsolateShim::QueueMicrotask(JsValueRef task) { | ||
JsAddRef(task, nullptr); | ||
this->microtaskQueue = new MicroTask(task, this->microtaskQueue); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if this fails? Do we leak the task?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "this" do you mean the new
allocation? If that fails, then things are probably in a bad way (this is only allocating 2 pointers worth of data) but I hadn't given much thought to that scenario. The previous behavior was that we would push the task into an array in javascript, which I guess would OOM in the worst case and terminate the process, while here if the allocation fails then we leak any prior tasks, and also never run them.
this->microtaskQueue = nullptr; | ||
|
||
// Ensure that we handle things in-order | ||
MicroTask* ordered = nullptr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't follow- what is the intent here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Microtasks are supposed to run in the order that they are queued. For simplicity I've just implemented a stack, but to operate in FIFO order I just reverse the stack here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use a tail pointer and append to the end (i.e implement an actual queue)
I'd also just settle for a less terse comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated the comment
t = next; | ||
} | ||
|
||
while (ordered != nullptr) {; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rogue semicolon?
@@ -164,6 +167,12 @@ class IsolateShim { | |||
void SetPromiseRejectCallback(v8::PromiseRejectCallback callback); | |||
|
|||
private: | |||
struct MicroTask { | |||
MicroTask(JsValueRef task, MicroTask* next) : task(task), next(next) {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we have this manage the lifetime of the JsValueRef?
// Loop until we've handled all the tasks, including the ones | ||
// added by these tasks | ||
while (this->microtaskQueue != nullptr) { | ||
MicroTask* batch = this->microtaskQueue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend using std::vector and then using std::swap to get the batch:
std::vector<MicroTask> batch;
std::swap(this->microtaskQueue, batch);
9f3390d
to
d2e0fcb
Compare
9e8c0cb
to
f08c5fd
Compare
} | ||
|
||
void IsolateShim::QueueMicrotask(JsValueRef task) { | ||
this->microtaskQueue.emplace_back(task); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kfarnung Would you mind double checking this is correct? I'm intending that this constructs the MicroTask
in place in the vector
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks correct to me.
std::vector<MicroTask> batch; | ||
std::swap(batch, this->microtaskQueue); | ||
|
||
for (unsigned int i = 0; i < batch.size(); ++i) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You might be able to do:
for (const MicroTask& microTask : batch) {
which provides a const reference to the item in the vector (and thus doesn't copy it)
std::swap(batch, this->microtaskQueue); | ||
|
||
for (unsigned int i = 0; i < batch.size(); ++i) { | ||
JsValueRef notUsed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Probably still worth initializing to JS_INVALID_REFERENCE
@@ -164,6 +167,24 @@ class IsolateShim { | |||
void SetPromiseRejectCallback(v8::PromiseRejectCallback callback); | |||
|
|||
private: | |||
struct MicroTask { | |||
MicroTask(JsValueRef task) : task(task) { | |||
JsAddRef(task, nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't matter, but maybe use this->task
to disambiguate?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
e584a17
to
e32cb2f
Compare
Initially we were handling microtasks per-context since our promise interface relies on per-library callbacks, but the v8 API actually deals with microtasks on a per-isolate level. This meant that we weren't properly firing microtasks / promise resolutions from contexts other than the currently active one. Now we also handle the microtask queue at the isolate level. PR-URL: nodejs#539 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Hitesh Kanwathirtha <hiteshk@microsoft.com>
Third time's a charm: https://ci.nodejs.org/job/chakracore-test-pull-request/261/ |
e32cb2f
to
4926aa5
Compare
Initially we were handling microtasks per-context since our promise interface relies on per-library callbacks, but the v8 API actually deals with microtasks on a per-isolate level. This meant that we weren't properly firing microtasks / promise resolutions from contexts other than the currently active one. Now we also handle the microtask queue at the isolate level. PR-URL: nodejs#539 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Hitesh Kanwathirtha <hiteshk@microsoft.com>
Initially we were handling microtasks per-context since our promise interface relies on per-library callbacks, but the v8 API actually deals with microtasks on a per-isolate level. This meant that we weren't properly firing microtasks / promise resolutions from contexts other than the currently active one. Now we also handle the microtask queue at the isolate level. PR-URL: nodejs#539 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Hitesh Kanwathirtha <hiteshk@microsoft.com>
4926aa5
to
576bc86
Compare
Initially we were handling microtasks per-context since our promise interface relies on per-library callbacks, but the v8 API actually deals with microtasks on a per-isolate level. This meant that we weren't properly firing microtasks / promise resolutions from contexts other than the currently active one. Now we also handle the microtask queue at the isolate level. PR-URL: nodejs#539 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Hitesh Kanwathirtha <hiteshk@microsoft.com>
Initially we were handling microtasks per-context since our promise interface relies on per-library callbacks, but the v8 API actually deals with microtasks on a per-isolate level. This meant that we weren't properly firing microtasks / promise resolutions from contexts other than the currently active one. Now we also handle the microtask queue at the isolate level. PR-URL: nodejs#539 Reviewed-By: Kyle Farnung <kfarnung@microsoft.com> Reviewed-By: Hitesh Kanwathirtha <hiteshk@microsoft.com>
Initially we were handling microtasks per-context since our promise
interface relies on per-library callbacks, but the v8 API actually
deals with microtasks on a per-isolate level. This meant that we
weren't properly firing microtasks / promise resolutions from
contexts other than the currently active one.
Now we also handle the microtask queue at the isolate level.
Partially addresses #420 in that promises should now work in
vm.runInContext
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes