-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
|
||
|
||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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> | ||
</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> | ||
|
||
|
@@ -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> | ||
|
@@ -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> | ||
|
||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. share-to-able read a little awkward. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep, any suggestions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe "connected event loops" and "transitive-connected event loops"? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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". There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
|
||
|
||
|
There was a problem hiding this comment.
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.)There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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).
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.