Skip to content
This repository has been archived by the owner on Oct 15, 2020. It is now read-only.

deps: fixing microtask behavior with multiple contexts #539

Merged
merged 1 commit into from
May 21, 2018

Conversation

MSLaguana
Copy link
Contributor

@MSLaguana MSLaguana commented May 18, 2018

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), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@MSLaguana MSLaguana requested a review from kfarnung May 18, 2018 19:19
@MSLaguana
Copy link
Contributor Author

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.

@MSLaguana
Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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;
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

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) {;
Copy link
Contributor

@kfarnung kfarnung May 18, 2018

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) {}
Copy link
Contributor

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;
Copy link
Contributor

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);

@MSLaguana MSLaguana force-pushed the fixContextMicrotasks branch from 9f3390d to d2e0fcb Compare May 21, 2018 16:06
@MSLaguana
Copy link
Contributor Author

@MSLaguana MSLaguana force-pushed the fixContextMicrotasks branch from 9e8c0cb to f08c5fd Compare May 21, 2018 16:26
}

void IsolateShim::QueueMicrotask(JsValueRef task) {
this->microtaskQueue.emplace_back(task);
Copy link
Contributor Author

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

Copy link
Contributor

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) {
Copy link
Contributor

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;
Copy link
Contributor

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);
Copy link
Contributor

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?

Copy link
Contributor

@kfarnung kfarnung left a comment

Choose a reason for hiding this comment

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

LGTM

@MSLaguana MSLaguana force-pushed the fixContextMicrotasks branch from e584a17 to e32cb2f Compare May 21, 2018 18:14
MSLaguana added a commit to MSLaguana/node-chakracore that referenced this pull request May 21, 2018
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>
@MSLaguana
Copy link
Contributor Author

@MSLaguana MSLaguana force-pushed the fixContextMicrotasks branch from e32cb2f to 4926aa5 Compare May 21, 2018 20:06
MSLaguana added a commit to MSLaguana/node-chakracore that referenced this pull request May 21, 2018
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>
@MSLaguana MSLaguana force-pushed the fixContextMicrotasks branch from 4926aa5 to 576bc86 Compare May 21, 2018 20:12
@MSLaguana MSLaguana merged commit 576bc86 into nodejs:master May 21, 2018
@MSLaguana MSLaguana deleted the fixContextMicrotasks branch May 21, 2018 20:14
kfarnung pushed a commit to kfarnung/node-chakracore that referenced this pull request May 22, 2018
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>
kfarnung pushed a commit to kfarnung/node-chakracore that referenced this pull request May 22, 2018
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>
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants