| Index: test/mjsunit/harmony/futex.js
|
| diff --git a/test/mjsunit/harmony/futex.js b/test/mjsunit/harmony/futex.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c7e1f5ce2a86b6fe50b9609781b1eb822f302fec
|
| --- /dev/null
|
| +++ b/test/mjsunit/harmony/futex.js
|
| @@ -0,0 +1,274 @@
|
| +// Copyright 2015 the V8 project authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// Flags: --allow-natives-syntax --harmony-atomics --harmony-sharedarraybuffer
|
| +
|
| +(function TestFailsWithNonSharedArray() {
|
| + var ab = new ArrayBuffer(16);
|
| +
|
| + var i8a = new Int8Array(ab);
|
| + var i16a = new Int16Array(ab);
|
| + var i32a = new Int32Array(ab);
|
| + var ui8a = new Uint8Array(ab);
|
| + var ui8ca = new Uint8ClampedArray(ab);
|
| + var ui16a = new Uint16Array(ab);
|
| + var ui32a = new Uint32Array(ab);
|
| + var f32a = new Float32Array(ab);
|
| + var f64a = new Float64Array(ab);
|
| +
|
| + [i8a, i16a, i32a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
|
| + ta) {
|
| + assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
|
| + assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
|
| + assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
|
| + });
|
| +})();
|
| +
|
| +(function TestFailsWithNonSharedInt32Array() {
|
| + var sab = new SharedArrayBuffer(16);
|
| +
|
| + var i8a = new Int8Array(sab);
|
| + var i16a = new Int16Array(sab);
|
| + var ui8a = new Uint8Array(sab);
|
| + var ui8ca = new Uint8ClampedArray(sab);
|
| + var ui16a = new Uint16Array(sab);
|
| + var ui32a = new Uint32Array(sab);
|
| + var f32a = new Float32Array(sab);
|
| + var f64a = new Float64Array(sab);
|
| +
|
| + [i8a, i16a, ui8a, ui8ca, ui16a, ui32a, f32a, f64a].forEach(function(
|
| + ta) {
|
| + assertThrows(function() { Atomics.futexWait(ta, 0, 0); });
|
| + assertThrows(function() { Atomics.futexWake(ta, 0, 1); });
|
| + assertThrows(function() { Atomics.futexWakeOrRequeue(ta, 0, 1, 0, 0); });
|
| + });
|
| +})();
|
| +
|
| +(function TestInvalidIndex() {
|
| + var i32a = new Int32Array(new SharedArrayBuffer(16));
|
| +
|
| + // Valid indexes are 0-3.
|
| + [-1, 4, 100].forEach(function(invalidIndex) {
|
| + assertEquals(undefined, Atomics.futexWait(i32a, invalidIndex, 0));
|
| + assertEquals(undefined, Atomics.futexWake(i32a, invalidIndex, 0));
|
| + var validIndex = 0;
|
| + assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, invalidIndex, 0, 0,
|
| + validIndex));
|
| + assertEquals(undefined, Atomics.futexWakeOrRequeue(i32a, validIndex, 0, 0,
|
| + invalidIndex));
|
| + });
|
| +
|
| +})();
|
| +
|
| +(function TestWaitTimeout() {
|
| + var i32a = new Int32Array(new SharedArrayBuffer(16));
|
| + var waitMs = 100;
|
| + var startTime = new Date();
|
| + assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, waitMs));
|
| + var endTime = new Date();
|
| + assertTrue(endTime - startTime >= waitMs);
|
| +})();
|
| +
|
| +(function TestWaitNotEqual() {
|
| + var i32a = new Int32Array(new SharedArrayBuffer(16));
|
| + assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 42));
|
| +})();
|
| +
|
| +(function TestWaitNegativeTimeout() {
|
| + var i32a = new Int32Array(new SharedArrayBuffer(16));
|
| + assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -1));
|
| + assertEquals(Atomics.TIMEDOUT, Atomics.futexWait(i32a, 0, 0, -Infinity));
|
| +})();
|
| +
|
| +//// WORKER ONLY TESTS
|
| +
|
| +if (this.Worker) {
|
| +
|
| + var TestWaitWithTimeout = function(timeout) {
|
| + var sab = new SharedArrayBuffer(16);
|
| + var i32a = new Int32Array(sab);
|
| +
|
| + var workerScript =
|
| + `onmessage = function(sab) {
|
| + var i32a = new Int32Array(sab);
|
| + var result = Atomics.futexWait(i32a, 0, 0, ${timeout});
|
| + postMessage(result);
|
| + };`;
|
| +
|
| + var worker = new Worker(workerScript);
|
| + worker.postMessage(sab, [sab]);
|
| +
|
| + // Spin until the worker is waiting on the futex.
|
| + while (%AtomicsFutexNumWaitersForTesting(i32a, 0) != 1) {}
|
| +
|
| + Atomics.futexWake(i32a, 0, 1);
|
| + assertEquals(Atomics.OK, worker.getMessage());
|
| + worker.terminate();
|
| + };
|
| +
|
| + // Test various infinite timeouts
|
| + TestWaitWithTimeout(undefined);
|
| + TestWaitWithTimeout(NaN);
|
| + TestWaitWithTimeout(Infinity);
|
| +
|
| +
|
| + (function TestWakeMulti() {
|
| + var sab = new SharedArrayBuffer(20);
|
| + var i32a = new Int32Array(sab);
|
| +
|
| + // SAB values:
|
| + // i32a[id], where id in range [0, 3]:
|
| + // 0 => Worker |id| is still waiting on the futex
|
| + // 1 => Worker |id| is not waiting on futex, but has not be reaped by the
|
| + // main thread.
|
| + // 2 => Worker |id| has been reaped.
|
| + //
|
| + // i32a[4]:
|
| + // always 0. Each worker is waiting on this index.
|
| +
|
| + var workerScript =
|
| + `onmessage = function(msg) {
|
| + var id = msg.id;
|
| + var i32a = new Int32Array(msg.sab);
|
| +
|
| + // Wait on i32a[4] (should be zero).
|
| + var result = Atomics.futexWait(i32a, 4, 0);
|
| + // Set i32a[id] to 1 to notify the main thread which workers were
|
| + // woken up.
|
| + Atomics.store(i32a, id, 1);
|
| + postMessage(result);
|
| + };`;
|
| +
|
| + var id;
|
| + var workers = [];
|
| + for (id = 0; id < 4; id++) {
|
| + workers[id] = new Worker(workerScript);
|
| + workers[id].postMessage({sab: sab, id: id}, [sab]);
|
| + }
|
| +
|
| + // Spin until all workers are waiting on the futex.
|
| + while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
|
| +
|
| + // Wake up three waiters.
|
| + assertEquals(3, Atomics.futexWake(i32a, 4, 3));
|
| +
|
| + var wokenCount = 0;
|
| + var waitingId = 0 + 1 + 2 + 3;
|
| + while (wokenCount < 3) {
|
| + for (id = 0; id < 4; id++) {
|
| + // Look for workers that have not yet been reaped. Set i32a[id] to 2
|
| + // when they've been processed so we don't look at them again.
|
| + if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
|
| + assertEquals(Atomics.OK, workers[id].getMessage());
|
| + workers[id].terminate();
|
| + waitingId -= id;
|
| + wokenCount++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + assertEquals(3, wokenCount);
|
| + assertEquals(0, Atomics.load(i32a, waitingId));
|
| + assertEquals(1, %AtomicsFutexNumWaitersForTesting(i32a, 4));
|
| +
|
| + // Finally wake the last waiter.
|
| + assertEquals(1, Atomics.futexWake(i32a, 4, 1));
|
| + assertEquals(Atomics.OK, workers[waitingId].getMessage());
|
| + workers[waitingId].terminate();
|
| +
|
| + assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, 4));
|
| +
|
| + })();
|
| +
|
| + (function TestWakeOrRequeue() {
|
| + var sab = new SharedArrayBuffer(24);
|
| + var i32a = new Int32Array(sab);
|
| +
|
| + // SAB values:
|
| + // i32a[id], where id in range [0, 3]:
|
| + // 0 => Worker |id| is still waiting on the futex
|
| + // 1 => Worker |id| is not waiting on futex, but has not be reaped by the
|
| + // main thread.
|
| + // 2 => Worker |id| has been reaped.
|
| + //
|
| + // i32a[4]:
|
| + // always 0. Each worker will initially wait on this index.
|
| + //
|
| + // i32a[5]:
|
| + // always 0. Requeued workers will wait on this index.
|
| +
|
| + var workerScript =
|
| + `onmessage = function(msg) {
|
| + var id = msg.id;
|
| + var i32a = new Int32Array(msg.sab);
|
| +
|
| + var result = Atomics.futexWait(i32a, 4, 0, Infinity);
|
| + Atomics.store(i32a, id, 1);
|
| + postMessage(result);
|
| + };`;
|
| +
|
| + var workers = [];
|
| + for (id = 0; id < 4; id++) {
|
| + workers[id] = new Worker(workerScript);
|
| + workers[id].postMessage({sab: sab, id: id}, [sab]);
|
| + }
|
| +
|
| + // Spin until all workers are waiting on the futex.
|
| + while (%AtomicsFutexNumWaitersForTesting(i32a, 4) != 4) {}
|
| +
|
| + var index1 = 4;
|
| + var index2 = 5;
|
| +
|
| + // If futexWakeOrRequeue is called with the incorrect value, it shouldn't
|
| + // wake any waiters.
|
| + assertEquals(Atomics.NOTEQUAL,
|
| + Atomics.futexWakeOrRequeue(i32a, index1, 1, 42, index2));
|
| +
|
| + assertEquals(4, %AtomicsFutexNumWaitersForTesting(i32a, index1));
|
| + assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
|
| +
|
| + // Now wake with the correct value.
|
| + assertEquals(1, Atomics.futexWakeOrRequeue(i32a, index1, 1, 0, index2));
|
| +
|
| + // The workers that are still waiting should atomically be transferred to
|
| + // the new index.
|
| + assertEquals(3, %AtomicsFutexNumWaitersForTesting(i32a, index2));
|
| +
|
| + // The woken worker may not have been scheduled yet. Look for which thread
|
| + // has set its i32a value to 1.
|
| + var wokenCount = 0;
|
| + while (wokenCount < 1) {
|
| + for (id = 0; id < 4; id++) {
|
| + if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
|
| + wokenCount++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
|
| +
|
| + // Wake the remaining waiters.
|
| + assertEquals(3, Atomics.futexWake(i32a, index2, 3));
|
| +
|
| + // As above, wait until the workers have been scheduled.
|
| + wokenCount = 0;
|
| + while (wokenCount < 3) {
|
| + for (id = 0; id < 4; id++) {
|
| + if (Atomics.compareExchange(i32a, id, 1, 2) == 1) {
|
| + wokenCount++;
|
| + }
|
| + }
|
| + }
|
| +
|
| + assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index1));
|
| + assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a, index2));
|
| +
|
| + for (id = 0; id < 4; ++id) {
|
| + assertEquals(Atomics.OK, workers[id].getMessage());
|
| + workers[id].terminate();
|
| + }
|
| +
|
| + })();
|
| +
|
| +}
|
|
|