Skip to content
This repository has been archived by the owner on Aug 29, 2021. It is now read-only.

Normative: Parent completion ordering DFS invariant extension #159

Merged
merged 9 commits into from
Mar 22, 2021

Conversation

guybedford
Copy link
Collaborator

@guybedford guybedford commented Jan 29, 2021

This extension provides an approach to the case brought up in #158 where parent completion orderings entirely differing from the normal post-order execution might be seen as surprising behaviour.

This approach uses a new global execution ordering on async execution.

The async fulfillment is then split out into two separate phases: a first phase to gather the list of parent modules that are synchronously ready for execution at async fulfillment time of a dependent, and a second phase to execute this list in global execution-sorted order based on this ordering.

Async rejections are handled as previously.

This change only affects execution timings of deferred async executions, imposing this new post-ordering sort on them. If we consider module graphs where every module depends individually on a single module using top-level await, using the same well-defined post-order execution ordering on all sync parent subgraph execution completions that run together seems like a sensible adjustment to me to ensure predictable behaviour.

Fixes #158.

Further review / feedback very welcome. // cc @Ms2ger @littledan @MylesBorins @robpalme @sokra

spec.html Outdated Show resolved Hide resolved
@Ms2ger Ms2ger requested a review from codehag January 29, 2021 10:13
@Ms2ger
Copy link
Collaborator

Ms2ger commented Jan 29, 2021

Yulia: I think you've got more of the proposal paged in than me right now, so I'd appreciate it if you could take a look. Let me know if you prefer I try.

spec.html Outdated Show resolved Hide resolved
@guybedford
Copy link
Collaborator Author

guybedford commented Feb 2, 2021

I've rebased this to the PR bug fix in #159, and also separated out the cycle gathering on failure, as cycle errors are allowed to be sparse in the current module algorithm, it doesn't seem such an issue to ensure consistency by just relying on cycle root. Instead we can follow this up in a separate PR if necessary.

Thanks for feedback so far. The diff should now be a little simpler as well.

spec.html Outdated Show resolved Hide resolved
@guybedford
Copy link
Collaborator Author

@jorendorff I've included your suggestions - brings the diff down nicely as well.

@guybedford guybedford force-pushed the parent-completion-ordering branch 2 times, most recently from d13b0fc to 46b80ab Compare February 2, 2021 16:15
@codehag codehag self-requested a review February 2, 2021 19:40
@guybedford
Copy link
Collaborator Author

I've rebased this PR to the latest merge of the cycle root fix, should be much simpler to review. Further feedback welcome, I do think this is an important normative change to make. If it needs to go explicitly through the meeting that would be understandable.

Further feedback would be very welcome to understand implementer interest.

@sokra sokra mentioned this pull request Feb 4, 2021
Copy link
Collaborator

@codehag codehag left a comment

Choose a reason for hiding this comment

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

I have unfortunately run out of time this week to fully do this review. If it can wait a week, I will do it then -- or someone can also take over. I made a few small comments.

I want to still make sure that we don't inadvertently end up changing this behaviour: https://github.com/web-platform-tests/wpt/pull/26771/files

Guy says it shouldn't be an issue but I would like to take more time to verify the implications.

spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
Copy link
Collaborator

@codehag codehag left a comment

Choose a reason for hiding this comment

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

Still thinking about the order of evaluation. A couple of comments so far

spec.html Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
Copy link
Collaborator

@codehag codehag left a comment

Choose a reason for hiding this comment

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

Bar the issue I found with three tests, that appears to be a js shell issue (will verify that today), this looks fine and is probably a worthwhile change. It doesn't appear to have web platform implications so we are good there. I am setting this to approved.

Sketch implementation here: https://phabricator.services.mozilla.com/D105437

@guybedford
Copy link
Collaborator Author

@syg it would be great to get your review on this PR if you're able to take a look.

@guybedford
Copy link
Collaborator Author

Just to reiterate, this PR is only to ensure that all sync subgraphs that depend on async modules execute in the original ordering, and it doesn't affect any of the other execution properties of this proposal that have already been agreed.

spec.html Show resolved Hide resolved
Co-authored-by: Ms2ger <Ms2ger@gmail.com>
spec.html Outdated Show resolved Hide resolved
Co-authored-by: Jordan Harband <ljharb@gmail.com>
@syg
Copy link
Contributor

syg commented Mar 12, 2021

I started implementing this and ran into a concern. I'd like the postorder position to be scoped to each top-level call to Evaluate(). However, this doesn't seem possible because the same async module can participate in concurrent, multiple top-level calls to Evaluate(), e.g. multiple <script> tags. That presents a problem: the postorder would need to be maintained as a global serial number or something, which puts it at higher risk of overflowing. There can be cleverer designs, I suppose, of resetting the global serial to 0 after all outstanding modules are finished, but that's too much complexity for my liking.

Edit: Perhaps I was premature on this concern: since the modules are always guaranteed to execute in global ascending order of when they first had their [[AsyncEvaluating]] set to true, in the global serial number approach, once a module finishes evaluating such that its ordinal is >= the global serial number, the global can be reset to 0. I think that works, but probably should draw up a proof to be safe.

@syg
Copy link
Contributor

syg commented Mar 13, 2021

Another issue: what actually resolves the top level Promise?

spec.html Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
spec.html Outdated Show resolved Hide resolved
@guybedford
Copy link
Collaborator Author

@syg nicely spotted - yes with the flattening of the previous recursive algorithm we need to now explicitly call the capabilities on the sync module parents of async modules.

Further to your suggestion, the fix can possibly actually resolve capabilities on all sync parents of async modules, as they could all have eg a later dynamic import expression that attached to those modules specifically. For this reason I've added the capability resolution within the main exec loop to all sync modules.

I also went ahead and moved the main "top level parent" capability resolution to the top, this is to ensure that the current module promise will resolve before the sync parent promises. Since all capability resolutions, I believe, wiill go into the task queue, the resolution functions can't be called synchronously. So having the capability resolution at the top should not break the sync invariant between the parent executions by calling a resolve function earlier than the parent executions. Please verify that I'm correct in that assumption though, and just let me know if further clarification would help.

Will also await on @codehag when she gets back for review before merging so we have time here too.

@syg
Copy link
Contributor

syg commented Mar 15, 2021

Further to your suggestion, the fix can possibly actually resolve capabilities on all sync parents of async modules, as they could all have eg a later dynamic import expression that attached to those modules specifically.

Great point!

@syg
Copy link
Contributor

syg commented Mar 16, 2021

If someone wants to an executable implementation in V8 to check things with, here's the CL: https://chromium-review.googlesource.com/c/v8/v8/+/2757901

@sokra
Copy link

sokra commented Mar 16, 2021

If somebody knows how to build a node.js version from that, you could run https://github.com/evanw/tla-fuzzer with that change and check the result. That's how I found the original case.

lazyparser pushed a commit to riscv-collab/v8 that referenced this pull request Mar 19, 2021
…subgraphs

This CL implements
tc39/proposal-top-level-await#159, which reached
consensus at the March 2021 TC39.

The high-level intent is for parent modules that depend on async modules
to remember the DFS post-order such that when their async dependency
finishes, they execute in that original post-order. This aligns the
ordering between completely sync module graphs and async module graphs.

Bug: v8:11557
Change-Id: I5bd8f38f040115c255ca1ce8253b9686fdb4af03
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2757901
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Adam Klein <adamk@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73551}
@guybedford guybedford requested a review from codehag March 21, 2021 11:10
Copy link
Collaborator

@codehag codehag left a comment

Choose a reason for hiding this comment

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

Looks good to me.

@guybedford
Copy link
Collaborator Author

I tested the patch in Node.js and can confirm it fixes #158 and has the correct timings for dynamic import registrations with the latest commit as well.

@guybedford guybedford merged commit 3f566ac into tc39:main Mar 22, 2021
@NemoStein NemoStein mentioned this pull request May 11, 2021
jugglinmike added a commit to bocoup/test262 that referenced this pull request May 14, 2021
This behavior was introduced after the Top-Level Await proposal reached
stage 3: tc39/proposal-top-level-await#159
jugglinmike added a commit to bocoup/test262 that referenced this pull request May 14, 2021
This behavior was introduced after the Top-Level Await proposal reached
stage 3: tc39/proposal-top-level-await#159
jugglinmike added a commit to tc39/test262 that referenced this pull request May 14, 2021
This behavior was introduced after the Top-Level Await proposal reached
stage 3: tc39/proposal-top-level-await#159
@jugglinmike
Copy link

We've merged a test for the new semantics to Test262: tc39/test262#2989

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.

Unexpected execution order with multiple parents
8 participants