Discussion:
Event loops in navigated-away-from windows
Boris Zbarsky
2014-09-27 02:03:40 UTC
Permalink
Now that JS is growing an event loop, what should happen to it in
navigated-away-from windows?

To make this concrete:

1) Say I have an object that came from such a window and it has an
Object.observe observer on it that mutates the property being observed.
Should this continue to run forever ever after the window is navigated
away from?

2) Say someone runs this in a web page:

(function f() Promise.resolve().then(f))()

what should happen when the user navigates away from that web page and why?

-Boris
Anne van Kesteren
2014-09-27 06:20:25 UTC
Permalink
Post by Boris Zbarsky
Now that JS is growing an event loop, what should happen to it in
navigated-away-from windows?
I still don't understand why JavaScript needs to have the loop itself
and can't just queue jobs for the Host to take care of. Given that the
Host has a more complicated model, that would be much saner and
wouldn't cause all this confusion of jobs vs microtasks vs tasks.
--
https://annevankesteren.nl/
Mark S. Miller
2014-09-27 13:54:51 UTC
Permalink
What confusion is being caused? AFAICT, this change is causing only the
clarification of things that were/are confusing, such as Boris' question.
Post by Anne van Kesteren
Post by Boris Zbarsky
Now that JS is growing an event loop, what should happen to it in
navigated-away-from windows?
I still don't understand why JavaScript needs to have the loop itself
and can't just queue jobs for the Host to take care of. Given that the
Host has a more complicated model, that would be much saner and
wouldn't cause all this confusion of jobs vs microtasks vs tasks.
--
https://annevankesteren.nl/
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140927/ff93cc4d/attachment.html>
Anne van Kesteren
2014-09-27 13:58:17 UTC
Permalink
Post by Mark S. Miller
What confusion is being caused? AFAICT, this change is causing only the
clarification of things that were/are confusing, such as Boris' question.
Well, I for one find it confusing that while HTML had a fairly worked
out event loop concept, ECMAScript added another and now I somehow
mentally need to integrate them. It would be way clearer if ECMAScript
just queued tasks/jobs/microtasks to the Host so we'd keep a single
concept of a loop.
--
https://annevankesteren.nl/
Ian Hickson
2014-09-29 18:18:36 UTC
Permalink
Post by Anne van Kesteren
Well, I for one find it confusing that while HTML had a fairly worked
out event loop concept, ECMAScript added another and now I somehow
mentally need to integrate them. It would be way clearer if ECMAScript
just queued tasks/jobs/microtasks to the Host so we'd keep a single
concept of a loop.
Allen and I discussed how they should be integrated, and the long and
short of it is that there's only one event loop; HTML just interrupts the
ES6 loop at NextJob step 4 (the "implementation defined manner"), and
resumes the HTML event loop, and when the HTML event loop needs to resume
running code, it resumes the NextJob algorithm. That and a few other hooks
ensures that all the jobs end up as tasks and all the ordering semantics
are preserved.

The discussion was at:
http://esdiscuss.org/topic/the-initialization-steps-for-web-browsers

I haven't yet done this in HTML because I'm waiting for Allen to make the
changes he talked about in that thread; this is being tracked here:
https://bugs.ecmascript.org/show_bug.cgi?id=3138

Once that's done I can update HTML. (I don't want to update HTML before,
because otherwise I'll have to do it twice.)

The HTML bug for this is:
https://www.w3.org/Bugs/Public/show_bug.cgi?id=25981

I certainly wouldn't object to the ES spec's event loop algorithms being
turned inside out (search for "RunCode" on the esdiscuss thread above for
an e-mail where I propose this) but that would be purely an editorial
change, it wouldn't change the implementations.
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
Anne van Kesteren
2014-09-29 21:08:13 UTC
Permalink
Post by Ian Hickson
I certainly wouldn't object to the ES spec's event loop algorithms being
turned inside out (search for "RunCode" on the esdiscuss thread above for
an e-mail where I propose this) but that would be purely an editorial
change, it wouldn't change the implementations.
The proposed setup from Allen will start failing the moment ECMAScript
wants something more complicated with its loop. At that point you'll
have to propose another set of hacks to make the integration with HTML
work again. And given this integration is so weird, I doubt
implementations will match it as written. Seems more likely they'll
implement the more straightforward alternative.

(Also, the proposed setup does seem to require exactly that kind of
mental integration I was worried about. With HTML hijacking the ES
loop to do its bidding.)
--
https://annevankesteren.nl/
Ian Hickson
2014-09-29 21:22:39 UTC
Permalink
Post by Anne van Kesteren
Post by Ian Hickson
I certainly wouldn't object to the ES spec's event loop algorithms
being turned inside out (search for "RunCode" on the esdiscuss thread
above for an e-mail where I propose this) but that would be purely an
editorial change, it wouldn't change the implementations.
The proposed setup from Allen will start failing the moment ECMAScript
wants something more complicated with its loop. At that point you'll
have to propose another set of hacks to make the integration with HTML
work again. And given this integration is so weird, I doubt
implementations will match it as written. Seems more likely they'll
implement the more straightforward alternative.
(Also, the proposed setup does seem to require exactly that kind of
mental integration I was worried about. With HTML hijacking the ES loop
to do its bidding.)
Certainly editorially I would much rather have the "inside out" version of
the spec hooks that I mentioned in my earlier e-mail, yes.
--
Ian Hickson U+1047E )\._.,--....,'``. fL
http://ln.hixie.ch/ U+263A /, _.. \ _\ ;`._ ,.
Things that are impossible just take longer. `._.-(,_..'--(,_..'`-.;.'
David Bruant
2014-09-30 10:56:00 UTC
Permalink
Post by Anne van Kesteren
Post by Ian Hickson
I certainly wouldn't object to the ES spec's event loop algorithms being
turned inside out (search for "RunCode" on the esdiscuss thread above for
an e-mail where I propose this) but that would be purely an editorial
change, it wouldn't change the implementations.
The proposed setup from Allen will start failing the moment ECMAScript
wants something more complicated with its loop.
How likely is this?

David
Mark S. Miller
2014-09-27 13:52:53 UTC
Permalink
What happens if someone runs

(function f() {setImmediate(f);})();

in a web page?
Post by Boris Zbarsky
Now that JS is growing an event loop, what should happen to it in
navigated-away-from windows?
1) Say I have an object that came from such a window and it has an
Object.observe observer on it that mutates the property being observed.
Should this continue to run forever ever after the window is navigated away
from?
(function f() Promise.resolve().then(f))()
what should happen when the user navigates away from that web page and why?
-Boris
_______________________________________________
es-discuss mailing list
es-discuss at mozilla.org
https://mail.mozilla.org/listinfo/es-discuss
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140927/b9bcd1a6/attachment.html>
Anne van Kesteren
2014-09-27 13:56:22 UTC
Permalink
Post by Mark S. Miller
What happens if someone runs
(function f() {setImmediate(f);})();
in a web page?
As far as I can tell from
https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate
that seems like a proprietary API from Microsoft. What would it do at
a specification level?
--
https://annevankesteren.nl/
Mark S. Miller
2014-09-27 14:02:14 UTC
Permalink
My intent was not to refer to a nonstd api. Instead, what happens with

(function f() {setTimeout(f, 0);})();

in a web page that is navigated away from?
Post by Anne van Kesteren
Post by Mark S. Miller
What happens if someone runs
(function f() {setImmediate(f);})();
in a web page?
As far as I can tell from
https://developer.mozilla.org/en-US/docs/Web/API/Window.setImmediate
that seems like a proprietary API from Microsoft. What would it do at
a specification level?
--
https://annevankesteren.nl/
--
Cheers,
--MarkM
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20140927/6cf8d781/attachment.html>
Anne van Kesteren
2014-09-27 14:49:56 UTC
Permalink
Post by Mark S. Miller
My intent was not to refer to a nonstd api. Instead, what happens with
(function f() {setTimeout(f, 0);})();
in a web page that is navigated away from?
Now we get to why having two loops is bad...

Tasks in a browsing context's event loop have an associated document.
If that document is not fully active, those tasks are skipped when
selecting the oldest task to run for that event loop. Meaning that if
you navigate away the tasks for the document you navigated away from
stop executing, until you navigate back to it.

https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-9
covers this processing model.
--
https://annevankesteren.nl/
Boris Zbarsky
2014-09-27 16:00:36 UTC
Permalink
Post by Mark S. Miller
My intent was not to refer to a nonstd api. Instead, what happens with
(function f() {setTimeout(f, 0);})();
in a web page that is navigated away from?
See
https://html.spec.whatwg.org/multipage/webappapis.html#processing-model-9 step
1 and also, if that were not enough,
https://html.spec.whatwg.org/multipage/webappapis.html#dom-windowtimers-settimeout
step 11, both of which refer to the concept of "fully active" documents.

-Boris
Boris Zbarsky
2014-09-27 15:57:48 UTC
Permalink
Post by Mark S. Miller
What happens if someone runs
(function f() {setImmediate(f);})();
in a web page?
You get into
https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/setImmediate/Overview.html#processingmodel
step 5, which waits forever (modulo back/forward caches) because the
document is not fully active.

-Boris
Boris Zbarsky
2014-10-15 02:50:12 UTC
Permalink
Post by Boris Zbarsky
(function f() Promise.resolve().then(f))()
what should happen when the user navigates away from that web page and why?
Given the lack of response from other implementors, I guess we'll just
implement whatever is simplest in Gecko for now...

-Boris
Adam Klein
2014-10-15 17:10:59 UTC
Permalink
Post by Boris Zbarsky
Post by Boris Zbarsky
(function f() Promise.resolve().then(f))()
what should happen when the user navigates away from that web page and why?
Given the lack of response from other implementors, I guess we'll just
implement whatever is simplest in Gecko for now...
Sorry for my delay in responding. Can you say what is simplest for Gecko in
this case? One of the reasons I didn't respond to the thread immediately is
that from my perspective Chromium has fairly ill-defined behavior for
navigated-away pages, due to the fact that they often die due to factors
that can't be explained by the platform (e.g., their host process dies). So
I'd be interested in what makes sense for Gecko and whether it might just
happen to be compatible with what we do in Chromium/Blink/V8.

Also, for case (1), is this all happening within one frame, or is this a
cross-frame example?

- Adam
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141015/a8865303/attachment.html>
Boris Zbarsky
2014-10-15 18:24:23 UTC
Permalink
Post by Adam Klein
Sorry for my delay in responding. Can you say what is simplest for Gecko
in this case?
We're still deciding that, but probably something like this: since in
Gecko every Promise is associated with a global from the point when it's
created (this is the global whose Promise.prototype is used as the
proto), we can hang off each Window the list of Promises for that
window, and then when we decide the Window is no longer active we'd just
mark all those promises as inactive as well, so they will not invoke any
callbacks.
Post by Adam Klein
One of the reasons I didn't respond to the thread
immediately is that from my perspective Chromium has fairly ill-defined
behavior for navigated-away pages
Sure. Other browsers do as well.
Post by Adam Klein
due to the fact that they often die
due to factors that can't be explained by the platform (e.g., their host
process dies
That's not observable, really. The observable case is when someone is
still holding references to objects in the navigated-away-from page and
manipulating them, and in your case that would mean the process is very
much alive.

Note that there are actually two somewhat interesting cases here: a page
being navigated away from, and the browsing context the page is in being
torn down entirely. Thing location.href set on an iframe for the former
case, and the <iframe> being removed from the DOM for the latter case.

In my ideal world, the two cases would behave identically, of course.
Post by Adam Klein
Also, for case (1), is this all happening within one frame, or is this a
cross-frame example?
The latter is more interesting to me personally, since I think it's the
harder problem.

-Boris
Allen Wirfs-Brock
2014-10-15 19:05:19 UTC
Permalink
Post by Boris Zbarsky
(function f() Promise.resolve().then(f))()
what should happen when the user navigates away from that web page and why?
Given the lack of response from other implementors, I guess we'll just implement whatever is simplest in Gecko for now...
From an ECMAScript perspective we need to reason about this in terms of realms and vats and ES jobs.

As a reminder:

A Vat is what ECMA262 defines. It is a self contained ES environment/processor/object space that has a single execution thread that runs ECMAScript evaluation "jobs" to completion. A Vat has a set of job queues containing jobs that are waiting to execute. When the current job is completed another waiting job is started. A Vat is a serialization boundary. Object references (ie, pointers) can not be directly exchanged/shared between Vats. Some sort of serialization/proxy mechanism needs to be used to communicate between Vats.

A Realm represents an ECMAScript GlobalEnviroment/ global object and all functions object whose scope includes that set of globals. A Vat may contain multiple Realms. Object references may be freely exchanged between Realms within the same Vat.

So, let's see if we can describe the behavior of:
(function f() Promise.resolve().then(f))()
in these terms:

The above expression must be evaluated as part of some job running in a Vat V. The code of the expression must be associated with some Realm R within V.
The function expression creates a new function object (let's call it f0) that is associated with Realm R this means that the reference to 'Promise' within its body will resolve to the Promise Global provided by Realm R.
The immediate call starts evaluating the body of f0 as part of the current job.
A new, already resolved (it's state is "fulfilled") promise (let's call it p0.0) is created.
Invoking the 'then' method on p0.0 notices that p.0.0 is in the "fulfilled" state, so it enqueues on V's PromiseJobs job queue a PromiseReactionJob with f0 as its reaction function. Let's call that pending job J0
'then' returns a new promise, p0.1 as the result of the 'then' method. p0.1 is in the "pending" state. It is the promise that will be resolved when the PromiseReactoinJob created in step 4 completes.
The returned reference to p0.1 is discarded, so no subsequent code can invoke methods on it and it has no PromiseReactions registered on it. So, when p0.1 is ultimately resolved, nothing will happen.
The current invocation of f0 returns and execution of the current job continues to completion.
A new job is selected from V's job queues and executed. This may occur 0 or more times until ultimately...
...eventually J0 is selection as the next job and execution of it starts
J0, as a PromiseReactionJob, invokes f0 as its handler, this starts evaluation of the the body of f0 as part of the current job
A new, already resolved (it's state is "fulfilled") promise (let's call it p1.0) is created.
Invoking the 'then' method on p1.0 notices that p.1.0 is in the "fulfilled" state, so it enqueues on V's PromiseJobs job queue a PromiseReactionJob with f0 as its reaction function. Let's call that pending job J1.
'then' returns a new promise, p1.1 as the result of the 'then' method. p1.1 is in the "pending" state. It is the promise that will be resolved when the PromiseReactoinJob created in step 12 completes.
The returned reference to p1.1 is discarded, so no subsequent code can invoke methods on it and it has no PromiseReactions registered on it. So, when p1.1 is ultimately resolved, nothing will happen.
The current invocation of f0 initiated in step 10 returns and execution of the current PromiseReactionJob completes. There are no longer any reachable references to p0.1 so it may not be garbage collected.
A new job is selected for execution from V's job queues. Eventually that will be J1.
Execution continues in this manner (essentially looping steps 8-16 with new Jn jobs) as long as V continues to execute jobs.

So, the basic question becomes one how the browser maps web pages to Vats. If each pages get a separate Vat then the above loop would presumably terminate when the user navigates away. If several "web pages" share a Vat then the loop would continue as long as any of those pages remained active. If the entire browser environment was represent as one vat then the loop continues as long as the browser is running. If a single Vat is used for multiple pages, a browser also has flexibility in how (and if) it selects ES jobs for execution, so it might impose some additional metadata and management policies on the Jn jobs. For example, perhaps it could purge all Jn jobs if Realm R is destroyed.

So, it all comes back to what is the mapping between various browser concepts and the above ES concepts. BTW, I'm not saying that the ES model can't evolve to make it easier to describe browser semantics, but this is our starting point for discussion.

Allen




-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141015/c04b4b8a/attachment.html>
Boris Zbarsky
2014-10-15 19:14:35 UTC
Permalink
Post by Allen Wirfs-Brock
The above expression must be evaluated as part of some job running in a
Vat V. The code of the expression must be associated with some Realm R
within V.
Correct.
Post by Allen Wirfs-Brock
17. Execution continues in this manner (essentially looping steps 8-16
with new Jn jobs) as long as V continues to execute jobs.
Right, this is the behavior that's not really desirable in the context
of the web.
Post by Allen Wirfs-Brock
So, the basic question becomes one how the browser maps web pages to
Vats. If each pages get a separate Vat
They don't. Basically, same-origin web pages correspond to multiple
Realms within a single Vat, since from their point of view direct object
references exist between them.

Worse yet, different-origin web pages might be able to become
same-origin (due to document.domain).

So in practice in a web browser a bunch of different web pages are all
separate Realms in the same Vat.
Post by Allen Wirfs-Brock
If the entire browser environment was represent
as one vat then the loop continues as long as the browser is running.
Yes, this is precisely the failure mode in Firefox right now.
Post by Allen Wirfs-Brock
For example, perhaps it could purge all Jn jobs if Realm R is destroyed.
You can't really "destroy" a realm while someone is holding references
to functions from that realm, yes?

What Gecko does do is mark a realm that corresponds to a
navigated-away-from web page (but NOT a web page loaded in an <iframe>
that is then removed from the DOM, for web compat reasons) as
"inactive", and Web IDL callbacks that are backed by functions from such
a Realm become no-ops. Functions in that Realm can still be called
directly, but not via a Web IDL callback.

Other browsers do different things here.
Post by Allen Wirfs-Brock
So, it all comes back to what is the mapping between various browser
concepts and the above ES concepts. BTW, I'm not saying that the ES
model can't evolve to make it easier to describe browser semantics, but
this is our starting point for discussion.
Sure.

-Boris
Allen Wirfs-Brock
2014-10-15 19:45:25 UTC
Permalink
Post by Allen Wirfs-Brock
...
So, the basic question becomes one how the browser maps web pages to
Vats. If each pages get a separate Vat
They don't. Basically, same-origin web pages correspond to multiple Realms within a single Vat, since from their point of view direct object references exist between them.
Worse yet, different-origin web pages might be able to become same-origin (due to document.domain).
So in practice in a web browser a bunch of different web pages are all separate Realms in the same Vat.
Right, but in practice, for different-origin pages, don't you use various forms of serialization/proxying to provide Vat-like object reference isolation? If you do that, then it feels like multiple Vats that share a single execution thread. We didn't current have that concept in ES, but we probably could.

Don't multi-process (eg, process per tab, etc.) browsers in fact have multiple execution threads?
Post by Allen Wirfs-Brock
If the entire browser environment was represent
as one vat then the loop continues as long as the browser is running.
Yes, this is precisely the failure mode in Firefox right now.
Post by Allen Wirfs-Brock
For example, perhaps it could purge all Jn jobs if Realm R is destroyed.
You can't really "destroy" a realm while someone is holding references to functions from that realm, yes?
What Gecko does do is mark a realm that corresponds to a navigated-away-from web page (but NOT a web page loaded in an <iframe> that is then removed from the DOM, for web compat reasons) as "inactive", and Web IDL callbacks that are backed by functions from such a Realm become no-ops. Functions in that Realm can still be called directly, but not via a Web IDL callback.
I intentionally used "destroy" as a ill defined, non-technical terms. What you describe above is along the lines of what I was thinking. A PromiseReactionJob that is associated with a marked Realm might plausibly by purged (perhaps by replacing its reaction function with a "Thrower", which would break the loop).

Regardless, it seems desirable to be able to describe what happens in terms of the specified Promise/Realm/Job semantics.


Allen
Boris Zbarsky
2014-10-15 20:36:31 UTC
Permalink
Post by Allen Wirfs-Brock
Right, but in practice, for different-origin pages, don't you use various forms of serialization/proxying to provide Vat-like object reference isolation?
Maybe.

First, note that being "different-origin" is not a static property due
to document.domain. In practice, things can start out different-origin
but become same-origin, if their origins are "related enough". Or start
same-origin and become different-origin.

Second, while Gecko does effectively have a proxy at each Realm boundary
(including same-origin Realms), other UAs do not. As a result, in some
UAs it's possible to get hold of an object that's not same-origin with
you at this moment, if it or other objects related to it were
same-origin with you in the past.
Post by Allen Wirfs-Brock
If you do that, then it feels like multiple Vats that share a single execution thread. We didn't current have that concept in ES, but we probably could.
That's true.

We do need to pin this down in some way, since this is all very much
observable in terms of event ordering, at least for things that can
reach each other, because it matters which things share event queues
with which other things.
Post by Allen Wirfs-Brock
Don't multi-process (eg, process per tab, etc.) browsers in fact have multiple execution threads?
Yes.

So do some single-process browsers (e.g. Presto-based Opera did, I think).
Post by Allen Wirfs-Brock
A PromiseReactionJob that is associated with a marked Realm
Are PromiseReactionJobs associated with a Realm at all? If so, how is
that Realm determined?
Post by Allen Wirfs-Brock
Regardless, it seems desirable to be able to describe what happens in terms of the specified Promise/Realm/Job semantics.
Agreed!

-Boris
Allen Wirfs-Brock
2014-10-15 21:10:11 UTC
Permalink
...
Post by Allen Wirfs-Brock
A PromiseReactionJob that is associated with a marked Realm
Are PromiseReactionJobs associated with a Realm at all? If so, how is that Realm determined?
Every PendingJob [1] has has a [[Realm]] field that is set to the realm of the active execution context when the job is enqueued [2]. For the PromiseResolutionJobs we are talking about that will be the realm of the 'then' method [3] that enqueues them. For the example I worked through that would be realm R, the realm of the active execution context when f0 is created in stop 1.

Allen

[1] http://people.mozilla.org/~jorendorff/es6-draft.html#sec-jobs-and-job-queues
[2] http://people.mozilla.org/~jorendorff/es6-draft.html#sec-enqueuejob
[3] http://people.mozilla.org/~jorendorff/es6-draft.html#sec-promise.prototype.then

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20141015/8a53d9ea/attachment.html>
Boris Zbarsky
2014-10-15 22:47:02 UTC
Permalink
Post by Allen Wirfs-Brock
Every PendingJob [1] has has a [[Realm]] field that is set to the realm
of the active execution context when the job is enqueued [2].
Ah, I see. That makes sense, thanks.
Post by Allen Wirfs-Brock
For the PromiseResolutionJobs we are talking about that will be the realm of the
'then' method [3] that enqueues them.
Yeah, agreed. That would certainly work in this case, in that we could
just stop running jobs associated with such a realm.

-Boris

Loading...