This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 25489 - [Imports]: Script execution order is non-deterministic in cyclic cases
Summary: [Imports]: Script execution order is non-deterministic in cyclic cases
Status: RESOLVED FIXED
Alias: None
Product: WebAppsWG
Classification: Unclassified
Component: HISTORICAL - Component Model (show other bugs)
Version: unspecified
Hardware: All All
: P2 normal
Target Milestone: ---
Assignee: Morrita Hajime
QA Contact: public-webapps-bugzilla
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-04-28 07:49 UTC by Gabor Krizsanits
Modified: 2014-04-29 15:24 UTC (History)
7 users (show)

See Also:


Attachments

Description Gabor Krizsanits 2014-04-28 07:49:53 UTC
As it is currently specced, it's hard to see why would the right edge be marked as cycle and not a random one from the cycle.

 M---> I1--->I4<-
 |\           | |
 | --> I2 <---- |
 |      |       |
 |      v       |
 ----> I3--------

Arrows represents links. If the parser is not blocked then the three edges of the circle between I2, I3, I4 might be found in random order. For example if between I1 and I4 there is a long chain of imports than I4->I2 directed path will be found late and because of it I4->I2 will be marked as cycle, since I3->I4 is already in, and at that point in time it was not considered as a cycle.
It's easy to see from here that, Execution order of I4 and I3 is based on luck mostly.

For me it's obvious that the intention should be to execute I3 first in this example and mark I3->I4 as the cycle, but speccing that way needs a bit more work and the algorithm might be significantly more complicated because of this...

Sorry for the ASCII, but I think this is the simplest way to illustrate the problem.
Comment 1 Dimitri Glazkov 2014-04-28 15:54:52 UTC
You might be right! There's a good discussion of import relationships (ancestor, parent, predecessor, dependent), but they don't seem to be used to ensure consistency.

The http://w3c.github.io/webcomponents/spec/imports/#dfn-import-request algorithm runs whenever a link is added to the document by the parser, so it has no insight into the overall graph of imports.
Comment 2 Morrita Hajime 2014-04-28 21:27:15 UTC
This is super interesting and seems like you're right. 
Thank you for pointing this out!

Cycles always make thing complex :-/

Instead of marking cycles, it might be better to distinguish 
"owning" link and "sharing" link (naming TBD). That is what Blink is kinda doing.
In this model, first link in the traversal order wins the "ownership" regardless
of the loading order.

Using your example, first let's consider

- I1 comes before I2 and
- I2 comes before I3 in the master M.
- and so on.

Based on this definition, the DFS traversal order of the tree (graph) is 

-  M->I1
- I1->I4
- I4->I2
- I3->I4 . This is marked as "shared" because I1->I4 already visited there.
-  M->I2 . This marked as "shared" as well.
-  M->I4 . Marked as shared.

This DFS traversal defines the spanning tree, that forms the import tree.

The point is that this ownership thing is "recomputed" each time new
import is added to the graph. For example, M->I2 first owns the I2, 
but once I4->I2 is found, the ownership is moved to I4->I2 from M->I2.

This might sound complicated, but thanks for the blocking of script execution,
we don't have to worry about undeterminisity of the script execution order.
It is (I believe) guaranteed to be executed by DFS order.

Thinking about I2 for example.
Imagine it is loaded by M2 at first. At that time, the graph would look like:

 M -> I1
   -> I2

The system cannot execute I2 because it is blocked by I1 which isn't yet loaded.

Then time passed, I4 was loaded by I1 and I4 found I2.
Here, I4 grabbed the ownership of I2 from M becaus I4->I2 wins M->I2 based on the DFS order.
Scripts in I2 (before link I2->I3) can be executed at this time 
because noting blocks I2 so far.

 M -> I1 -> I4
   .> I2 <-
   -> I3

Note that this diagram shows that I3 is also loaded by M.
It cannot be executed either because I1 blocks I3.

Then I2 found I3. I2 grabbed the ownership of I3 from M.

 M -> I1 -> I4
   .> I2 <-
       |
       v
   .> I3

Then I3 found I4, but it didn't own it.

 M -> I1 -> I4
   .> I2 <-
       |
       v
   .> I3  .>

And so on...

Hmm, apparently this explanation isn't clear enough :-/
I'll update the spec to give clearer picture.
Comment 3 Morrita Hajime 2014-04-29 00:27:46 UTC
OK, here is the attempt:
https://github.com/w3c/webcomponents/commit/a8e93907e54d577a2710ccedbf275ede43f0cb99

The change shouldn't change the basic behavior but just get rid of undeterminisity. I don't think the real implementations actually have to recompute the flag. It's just a clarification of the concept.

Any feedback is appreciated. Please feel free to reopen.
Comment 4 Gabor Krizsanits 2014-04-29 15:24:42 UTC
Yes, this version looks a lot better. Thanks for the quick reply :)