Skip to content

Commit

Permalink
Introduce $262.agent.safeBroadcast and migrate Atomics tests.
Browse files Browse the repository at this point in the history
Migrating all tests to this API is necessary to prevent tests from hanging indefinitely when a SAB is sent to a worker but the code in the worker attempts to create a non-sharable TypedArray (something that is not Int32Array or BigInt64Array). When that scenario occurs, an exception is thrown and the agent worker can no longer communicate with any other threads that control the SAB. If the main thread happens to be spinning in the $262.agent.waitUntil() while loop, it will never meet its termination condition and the test will hang indefinitely.

Because we've defined $262.agent.broadcast(SAB) in https://github.com/tc39/test262/blob/master/INTERPRETING.md, there are host implementations that assume compatibility, which must be maintained.
  • Loading branch information
rwaldron committed Nov 20, 2018
1 parent ba6a483 commit e488309
Show file tree
Hide file tree
Showing 60 changed files with 119 additions and 76 deletions.
21 changes: 11 additions & 10 deletions INTERPRETING.md
Expand Up @@ -66,24 +66,24 @@ properties of the global scope prior to test execution.
- **`global`** - a reference to the global object on which `$262` was initially defined
- **`IsHTMLDDA`** - (present only in implementations that can provide it) an
object that 1) has an [[IsHTMLDDA]] internal slot, and 2) when called with
no arguments or with the single argument `""` returns `null`. Use this
no arguments or with the single argument `""` returns `null`. Use this
property to test that ECMAScript algorithms aren't mis-implemented to treat
`document.all` as being `undefined` or of type Undefined (instead of
Object). (The peculiar second requirement permits testing algorithms when
Object). (The peculiar second requirement permits testing algorithms when
they also call `document.all` with such arguments, so that testing for
correct behavior requires knowing how the call behaves. This is rarely
correct behavior requires knowing how the call behaves. This is rarely
necessary.) Tests using this function must be tagged with the `IsHTMLDDA`
feature so that only hosts supporting this property will run them.
- **`agent`** - an ordinary object with the following properties:
- **`start`** - a function that takes a script source string and runs
the script in a concurrent agent. Will block until that agent is
running. The agent has no representation. The agent script will be
the script in a concurrent agent. Will block until that agent is
running. The agent has no representation. The agent script will be
run in an environment that has an object `$262` with a property `agent`
with the following properties:
- **`receiveBroadcast`** - a function that takes a function and
calls the function when it has received a broadcast from the parent,
passing it the broadcast as two arguments, a SharedArrayBuffer and
an Int32. This function may return before a broadcast is received
an Int32 or BigInt. This function may return before a broadcast is received
(eg to return to an event loop to await a message) and no code should
follow the call to this function.
- **`report`** - a function that accepts a single "message" argument,
Expand All @@ -93,10 +93,11 @@ properties of the global scope prior to test execution.
- **`leaving`** - a function that signals that the agent is done and
may be terminated (if possible).
- **`monotonicNow`** - a function that returns a value that conforms to [`DOMHighResTimeStamp`][] and is produced in such a way that its semantics conform to **[Monotonic Clock][]**.
- **`broadcast`** - a function that takes a SharedArrayBuffer and an Int32
and broadcasts the two values to all concurrent agents. The function
blocks until all agents have retrieved the message. Note, this assumes
that all agents that were started are still running.
- **`broadcast`** - a function that takes a SharedArrayBuffer and an
Int32 or BigInt and broadcasts the two values to all concurrent
agents. The function blocks until all agents have retrieved the
message. Note, this assumes that all agents that were started are
still running.
- **`getReport`** - a function that reads an incoming string from any agent,
and returns it if it exists, or returns `null` otherwise.
- **`sleep`** - a function that takes a millisecond argument and
Expand Down
50 changes: 46 additions & 4 deletions harness/atomicsHelper.js
Expand Up @@ -28,6 +28,47 @@ description: >
return r;
};
}

/**
*
* Share a given Int32Array or BigInt64Array to all running agents. Ensure that the
* provided TypedArray is a "shared typed array".
*
* NOTE: Migrating all tests to this API is necessary to prevent tests from hanging
* indefinitely when a SAB is sent to a worker but the code in the worker attempts to
* create a non-sharable TypedArray (something that is not Int32Array or BigInt64Array).
* When that scenario occurs, an exception is thrown and the agent worker can no
* longer communicate with any other threads that control the SAB. If the main
* thread happens to be spinning in the $262.agent.waitUntil() while loop, it will never
* meet its termination condition and the test will hang indefinitely.
*
* Because we've defined $262.agent.broadcast(SAB) in
* https://github.com/tc39/test262/blob/master/INTERPRETING.md, there are host implementations
* that assume compatibility, which must be maintained.
*
*
* $262.agent.safeBroadcast(TA) should not be included in
* https://github.com/tc39/test262/blob/master/INTERPRETING.md
*
*
* @param {(Int32Array|BigInt64Array)} typedArray An Int32Array or BigInt64Array with a SharedArrayBuffer
*/
$262.agent.safeBroadcast = function(typedArray) {
let Constructor = Object.getPrototypeOf(typedArray).constructor;
let temp = new Constructor(
new SharedArrayBuffer(Constructor.BYTES_PER_ELEMENT)
);
try {
// This will never actually wait, but that's fine because we only
// want to ensure that this typedArray CAN be waited on and is shareable.
Atomics.wait(temp, 0, Constructor === Int32Array ? 1 : BigInt(1));
} catch (error) {
$ERROR(`${Constructor.name} cannot be used as a shared typed array. (${error})`);
}

$262.agent.broadcast(typedArray.buffer);
};

/**
* With a given Int32Array or BigInt64Array, wait until the expected number of agents have
* reported themselves by calling:
Expand All @@ -39,6 +80,7 @@ description: >
* @param {number} expected The number of agents that are expected to report as active.
*/
$262.agent.waitUntil = function(typedArray, index, expected) {

var agents = 0;
while ((agents = Atomics.load(typedArray, index)) !== expected) {
/* nothing */
Expand Down Expand Up @@ -76,7 +118,7 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* $262.agent.leaving();
* });
* `);
* $262.agent.broadcast(i32a.buffer);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agent was started and then try to yield control to increase
* // the likelihood the agent has called `Atomics.wait` and is now waiting.
Expand Down Expand Up @@ -106,7 +148,7 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* });
* `);
* }
* $262.agent.broadcast(i32a.buffer);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agents were started and then try to yield control to increase
* // the likelihood the agents have called `Atomics.wait` and are now waiting.
Expand Down Expand Up @@ -151,7 +193,7 @@ $262.agent.waitUntil = function(typedArray, index, expected) {
* });
* `);
* }
* $262.agent.broadcast(i32a.buffer);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until the agents were started and then try to yield control to increase
* // the likelihood the agents have called `Atomics.wait` and are now waiting.
Expand Down Expand Up @@ -204,7 +246,7 @@ $262.agent.timeouts = {
* $262.agent.leaving();
* });
* `);
* $262.agent.broadcast(i32a.buffer);
* $262.agent.safeBroadcast(i32a.buffer);
*
* // Wait until agent was started and then try to yield control.
* $262.agent.waitUntil(i32a, RUNNING, 1);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/bigint/notify-all-on-loc.js
Expand Up @@ -57,7 +57,7 @@ const i64a = new BigInt64Array(
new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);

// Wait for agents to be running.
$262.agent.waitUntil(i64a, RUNNING, BigInt(NUMAGENT + 1));
Expand Down
Expand Up @@ -43,7 +43,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);

// An agent may have been interrupted between reporting its initial report
Expand Down
Expand Up @@ -41,7 +41,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);

// An agent may have been interrupted between reporting its initial report
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/negative-count.js
Expand Up @@ -26,7 +26,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, 1);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-all-on-loc.js
Expand Up @@ -57,7 +57,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT + 1);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-all.js
Expand Up @@ -30,7 +30,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-in-order-one-time.js
Expand Up @@ -42,7 +42,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-in-order.js
Expand Up @@ -42,7 +42,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-nan.js
Expand Up @@ -26,7 +26,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, 1);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-one.js
Expand Up @@ -34,7 +34,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-renotify-noop.js
Expand Up @@ -26,7 +26,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

$262.agent.waitUntil(i32a, RUNNING, 1);

Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-two.js
Expand Up @@ -34,7 +34,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
Expand Up @@ -24,7 +24,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, 1);

// There are ZERO agents waiting to notify...
Expand Down
Expand Up @@ -24,7 +24,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 2)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);
$262.agent.waitUntil(i32a, RUNNING, 1);

// There are ZERO matching agents waiting on index 1
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/notify/notify-zero.js
Expand Up @@ -34,7 +34,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * BUFFER_SIZE)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait for agents to be running.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
Expand Up @@ -50,7 +50,7 @@ const i32a = new Int32Array(
new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i32a.buffer);
$262.agent.safeBroadcast(i32a);

// Wait until both agents started.
$262.agent.waitUntil(i32a, RUNNING, NUMAGENT);
Expand Down
10 changes: 5 additions & 5 deletions test/built-ins/Atomics/wait/bigint/false-for-timeout-agent.js
Expand Up @@ -16,6 +16,10 @@ includes: [atomicsHelper.js]
features: [Atomics, BigInt, SharedArrayBuffer, TypedArray]
---*/

const i64a = new BigInt64Array(
new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT * 4)
);

const RUNNING = 1;

$262.agent.start(`
Expand Down Expand Up @@ -46,11 +50,7 @@ $262.agent.start(`
});
`);

const i64a = new BigInt64Array(
new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
2 changes: 1 addition & 1 deletion test/built-ins/Atomics/wait/bigint/nan-for-timeout.js
Expand Up @@ -33,7 +33,7 @@ const i64a = new BigInt64Array(
new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -25,7 +25,7 @@ const i64a = new BigInt64Array(
new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT * 4)
);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -38,7 +38,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down
Expand Up @@ -31,7 +31,7 @@ $262.agent.start(`
});
`);

$262.agent.broadcast(i64a.buffer);
$262.agent.safeBroadcast(i64a);
$262.agent.waitUntil(i64a, RUNNING, 1n);

// Try to yield control to ensure the agent actually started to wait.
Expand Down

0 comments on commit e488309

Please sign in to comment.