Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrate JavaScript SharedArrayBuffers and agents/agent clusters #2361

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 132 additions & 6 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,13 @@
be thought of as completely serializing the execution of all scripts in all <span data-x="browsing
context">browsing contexts</span>.</p>

<p>The exception to this general design principle is the JavaScript <code>SharedArrayBuffer</code>
class. Using <code>SharedArrayBuffer</code>s, it can in fact be observed that scripts in other
<span data-x="event loop">event loops</span> are executing simultaneously. Furthermore, due to the
JavaScript memory model, there are situations which not only are un-representable via serialized
<em>script</em> execution, but also un-representable via serialized <em>statement</em> execution
among those scripts.</p>

</div>


Expand Down Expand Up @@ -2865,6 +2872,8 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute

<ul class="brief">
<li><dfn data-x-href="https://tc39.github.io/ecma262/#active-function-object">active function object</dfn></li>
<li><dfn data-x-href="https://tc39.github.io/ecma262/#sec-agents">agent</dfn> and
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-agent-clusters">agent cluster</dfn></li>
<li><dfn data-x-href="https://tc39.github.io/ecma262/#sec-automatic-semicolon-insertion">automatic semicolon insertion</dfn></li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#current-realm">current Realm Record</dfn></li>
<li><dfn data-x-href="https://tc39.github.io/ecma262/#early-error-rule">early error</dfn></li>
Expand All @@ -2882,8 +2891,9 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<dfn>@@toStringTag</dfn></li>
<li><dfn data-x-href="https://tc39.github.io/ecma262/#sec-well-known-intrinsic-objects">Well-Known Intrinsic Objects</dfn>, including
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-arraybuffer-constructor">%ArrayBuffer%</dfn>,
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-properties-of-the-array-prototype-object">%ArrayPrototype%</dfn>, and
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-object.prototype.valueof">%ObjProto_valueOf%</dfn></li>
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-properties-of-the-array-prototype-object">%ArrayPrototype%</dfn>,
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-object.prototype.valueof">%ObjProto_valueOf%</dfn>, and
<dfn data-x-href="https://tc39.github.io/ecma262/#sec-sharedarraybuffer-constructor">%SharedArrayBuffer%</dfn></li>

<li>The <dfn data-x="js-prod-FunctionBody" data-x-href="https://tc39.github.io/ecma262/#prod-FunctionBody"><i>FunctionBody</i></dfn> production</li>
<li>The <dfn data-x="js-prod-Module" data-x-href="https://tc39.github.io/ecma262/#prod-Module"><i>Module</i></dfn> production</li>
Expand All @@ -2898,6 +2908,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<dfn data-x="js-ModuleEvaluation" data-x-href="https://tc39.github.io/ecma262/#sec-moduleevaluation">ModuleEvaluation</dfn> and
<dfn data-x="js-ModuleDeclarationInstantiation" data-x-href="https://tc39.github.io/ecma262/#sec-moduledeclarationinstantiation">ModuleDeclarationInstantiation</dfn> methods</li>

<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-allocatesharedarraybuffer">AllocateSharedArrayBuffer</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-arraycreate">ArrayCreate</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-call">Call</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-clonearraybuffer">CloneArrayBuffer</dfn> abstract operation</li>
Expand All @@ -2919,6 +2930,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-isconstructor">IsConstructor</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-isdatadescriptor">IsDataDescriptor</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-isdetachedbuffer">IsDetachedBuffer</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-issharedarraybuffer">IsSharedArrayBuffer</dfn> abstract operation</li>
<li>The <dfn data-x="js-NewObjectEnvironment" data-x-href="https://tc39.github.io/ecma262/#sec-newobjectenvironment">NewObjectEnvironment</dfn> abstract operation</li>
<!-- the next set of Ordinary* abstract operations are in order of appearance -->
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-ordinarygetprototypeof">OrdinaryGetPrototypeOf</dfn> abstract operation</li>
Expand All @@ -2944,6 +2956,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x="js-abstract-equality" data-x-href="https://tc39.github.io/ecma262/#sec-abstract-equality-comparison">Abstract Equality Comparison</dfn> algorithm</li>
<li>The <dfn data-x="js-strict-equality" data-x-href="https://tc39.github.io/ecma262/#sec-strict-equality-comparison">Strict Equality Comparison</dfn> algorithm</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-date-objects"><code>Date</code></dfn> class</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-sharedarraybuffer-objects"><code>SharedArrayBuffer</code></dfn> class</li>
<li>The <dfn data-x-href="https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-typeerror"><code>TypeError</code></dfn> class</li>
<li>The <dfn data-x="js-RangeError" data-x-href="https://tc39.github.io/ecma262/#sec-native-error-types-used-in-this-standard-rangeerror"><code>RangeError</code></dfn> class</li>
<li>The <dfn data-x="js-typeof" data-x-href="https://tc39.github.io/ecma262/#sec-typeof-operator"><code>typeof</code></dfn> operator</li>
Expand Down Expand Up @@ -7791,11 +7804,54 @@ interface <dfn>DOMStringList</dfn> {
<li><p>If <span>IsDetachedBuffer</span>(<var>input</var>) is true, then throw a
<span>"<code>DataCloneError</code>"</span> <code>DOMException</code>.</p></li>

<li><p>Let <var>outputArrayBuffer</var> be the <span>%ArrayBuffer%</span> intrinsic object in
<var>targetRealm</var>.
<li>
<p>If <span>IsSharedArrayBuffer</span>(<var>input</var>) is true, then:</p>

<ol>
<li>
<p>If <var>targetRealm</var>'s <span data-x="concept-realm-settings-object">settings
object</span>'s <span>responsible event loop</span> is not in the same <span>unit of
share-within-able event loops</span> as the <span>current Realm Record</span>'s <span
data-x="concept-realm-settings-object">settings object</span>'s <span>responsible event
loop</span>, then:</p>

<ol>
<li><p>Let <var>outputConstructor</var> be the <span>%SharedArrayBuffer%</span> intrinsic
object in <var>targetRealm</var>.</p></li>

<li><p>Let <var>output</var> be !
<span>AllocateSharedArrayBuffer</span>(<var>outputConstructor</var>, 0).</p></li>
</ol>

<p class="note">The reason that this step creates a zero-length
<code>SharedArrayBuffer</code> instance, instead of throwing an error, is due to the fact
that at the time <span>StructuredCloneWithTransfer</span> is performed, the
<var>targetRealm</var> is not always known, so we cannot synchronously throw an error to the
caller. (An example is when we use <code
data-x="dom-MessagePort-postMessage">postMessage()</code> on a <code>MessagePort</code>
whose messages are initially paused.) This subtlety is unfortunately obfuscated by the
current <span>StructuredCloneWithTransfer</span> algorithm; see <a
href="https://github.com/whatwg/html/issues/935">issue #935</a>.</p>
Copy link
Member

Choose a reason for hiding this comment

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

Is this really how we want to signal errors? Wouldn't it be much better if the message contained an indication of the error in some way? (Note that we also need to solve this for ArrayBuffer, as its allocation can fail.)

Copy link
Member

Choose a reason for hiding this comment

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

It's also a little sad that we don't solve the underlying issue of the model being wrong first. It makes it harder to see the correct solutions.

Copy link
Member Author

@domenic domenic Feb 16, 2017

Choose a reason for hiding this comment

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

My understanding is that this is how it was shipped in all browsers already, based on the spec this is integrating, but maybe I'm wrong.

Last time this was discussed in #936 the idea was to not mix up error messages and data.

The advantage of this design, by the way, is that it allows you to get all the other data you cloned/transferred, instead of throwing it away (in the transferred case, forever).

Copy link
Member

Choose a reason for hiding this comment

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

So is this how browsers deal with ArrayBuffer allocation failure?

Copy link
Member Author

Choose a reason for hiding this comment

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

In that case I think they mostly just crash.

Copy link
Member

Choose a reason for hiding this comment

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

Really? JavaScript prescribes a TypeError and I've seen that in code.

</li>

<li><p>Otherwise, let <var>output</var> be a new SharedArrayBuffer object in
<var>targetRealm</var> whose [[ArrayBufferData]] internal slot value is
<var>input</var>.[[ArrayBufferData]] and whose [[ArrayBufferByteLength]] internal slot value
is <var>input</var>.[[ArrayBufferByteLength]].</p></li>
</ol>
</li>

<li>
<p>Otherwise:</p>

<ol>
<li><p>Let <var>outputConstructor</var> be the <span>%ArrayBuffer%</span> intrinsic object in
<var>targetRealm</var>.</p></li>

<li><p>Let <var>output</var> be ? <span>CloneArrayBuffer</span>(<var>input</var>, 0,
<var>outputArrayBuffer</var>).</p></li>
<li><p>Let <var>output</var> be ? <span>CloneArrayBuffer</span>(<var>input</var>, 0,
<var>outputConstructor</var>).</p></li>
</ol>
</li>
</ol>
</li>

Expand Down Expand Up @@ -8064,6 +8120,8 @@ interface <dfn>DOMStringList</dfn> {
<ol>
<li><p>If <span>IsDetachedBuffer</span>(<var>O</var>) is true, then return false.</p></li>

<li><p>If <span>IsSharedArrayBuffer</span>(<var>O</var>) is true, then return false.</p></li>

<li><p>Return true.</p></li>
</ol>
</li>
Expand All @@ -8086,6 +8144,8 @@ interface <dfn>DOMStringList</dfn> {
data-x="transfer-abstract-op">Transfer</dfn> ( <var>input</var>, <var>targetRealm</var> )</h4>

<ol>
<li><p>Assert: <span>IsTransferable</span>(<var>input</var>) is true.</p></li>

<li>
<p>If <var>input</var> has an [[ArrayBufferData]] internal slot, then:</p>

Expand Down Expand Up @@ -87910,6 +87970,72 @@ import "https://example.com/foo/../module2.js";</pre>
data-x="concept-module-script-module-record">module record</span>.</p></li>
</ol>

<h5>Integration with the JavaScript agent formalism</h5>

<p>JavaScript defines the concept of an <span>agent</span>. This corresponds, in a 1-to-1 manner,
with this specification's concept of an <span>event loop</span>.</p>

<p>To determine the manifestation of JavaScript's <span>agent cluster</span> concept in user
agents, we first define the set of <dfn>directly share-to-able event loops</dfn> for a given
Copy link
Member

Choose a reason for hiding this comment

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

share-to-able read a little awkward.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, any suggestions?

Copy link
Member

Choose a reason for hiding this comment

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

Maybe "connected event loops" and "transitive-connected event loops"?

Copy link
Member Author

Choose a reason for hiding this comment

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

I like including something about "sharing" in the name since this is largely about shared memory but that is indeed less awkward so I'm happy to switch to "connected".

Copy link
Member

Choose a reason for hiding this comment

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

"Directly-shared event loops" and "shared event loops" seems okay too. I like it a little better as it has a shorter term for the thing that maps to JavaScript.

<span>event loop</span> <var>loop</var>:</p>

<dl class="switch">
<dt>For a <span>browsing context</span> <span>event loop</span></dt>
<dd>The set containing <var>loop</var>, plus the <a href="#worker-event-loop">worker event
loops</a> of any worker whose <span>environment settings object</span>'s <span>responsible
browsing context</span> is one of <var>loop</var>'s <span data-x="browsing context">browsing
contexts</span></dd>

<dt>For a <a href="#worker-event-loop">worker event loop</a> of a dedicated worker</dt>
<dd>The set containing <var>loop</var>, plus the worker's <span>environment settings
object</span>'s <span>responsible browsing context</span>'s <span>event loop</span>, plus the
<a href="#worker-event-loop">worker event loops</a> of each of <span>the worker's
workers</span></dd>
Copy link
Member

Choose a reason for hiding this comment

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

What if this worker was inside a service worker or a shared worker?

Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding is that all workers spawned from a given shared/service worker are share-to-able.

Copy link
Member

Choose a reason for hiding this comment

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

Sure, but what about its responsible browsing context then? That's either null or it should not share with that.

Copy link
Member Author

Choose a reason for hiding this comment

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

I see, yeah. Hopefully it is null, in which case this is a wording tweak, but I'll need to check.

Copy link
Member

Choose a reason for hiding this comment

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

It's never null.


<dt>For a <a href="#worker-event-loop">worker event loop</a> of any other type of worker</dt>
<dd>The set containing <var>loop</var>, plus the <a href="#worker-event-loop">worker event
loops</a> of each of <span>the worker's workers</span></dd>
</dl>

<p>Then, the transitive closure of all <span data-x="event loop">event loops</span> that are
<span>directly share-to-able event loops</span> forms a <dfn>unit of share-within-able event
loops</dfn>, and a JavaScript <span>agent cluster</span> consists of all the corresponding <span
data-x="agent">agents</span>.</p>

<p class="note">The <span>agent cluster</span> concept is crucial for defining the JavaScript
memory model, and in particular among which <span data-x="agent">agents</span> the backing data of
<code>SharedArrayBuffer</code> objects can be shared.</p>

<div class="example">
<p>The following pairs of global objects are each within the same <span>unit of share-within-able
event loops</span>, and thus can use <code>SharedArrayBuffer</code> instances to share memory
with each other:</p>

<ul class="brief">
<li>A <code>Window</code> and a dedicated worker that it created</li>

<li>A <code>Window</code> and the <code>Window</code> of a <span>same origin-domain</span>
<code>iframe</code></li>

<li>A <code>Window</code> and the <code>Window</code> that opened it</li>

<li>A worker (of any type) and any nested workers it creates</li>
</ul>

<p>The following pairs of global objects are <em>not</em> within the same <span>unit of
share-within-able event loops</span>, and thus cannot share memory:</p>

<ul class="brief">
<li>A <code>Window</code> and a shared worker it creates</li>

<li>A <code>Window</code> and a service worker it creates</li>

<li>A <code>Window</code> and the <code>Window</code> of a non-<span>same origin-domain</span>
<code>iframe</code></li>
</ul>

</div>

</div>


Expand Down