154ddde42b
BUG=v8:4555 LOG=N Review URL: https://codereview.chromium.org/1462833002 Cr-Commit-Position: refs/heads/master@{#32142}
341 lines
11 KiB
JavaScript
341 lines
11 KiB
JavaScript
// 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-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 sab = new SharedArrayBuffer(16);
|
|
var i32a = new Int32Array(sab);
|
|
|
|
// 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));
|
|
});
|
|
|
|
i32a = new Int32Array(sab, 8);
|
|
[-1, 2, 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 sab = new SharedArrayBuffer(16);
|
|
var i32a = new Int32Array(sab);
|
|
assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 42));
|
|
|
|
i32a = new Int32Array(sab, 8);
|
|
i32a[0] = 1;
|
|
assertEquals(Atomics.NOTEQUAL, Atomics.futexWait(i32a, 0, 0));
|
|
})();
|
|
|
|
(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(msg) {
|
|
var i32a = new Int32Array(msg.sab, msg.offset);
|
|
var result = Atomics.futexWait(i32a, 0, 0, ${timeout});
|
|
postMessage(result);
|
|
};`;
|
|
|
|
var worker = new Worker(workerScript);
|
|
worker.postMessage({sab: sab, offset: offset}, [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();
|
|
|
|
var worker2 = new Worker(workerScript);
|
|
var offset = 8;
|
|
var i32a2 = new Int32Array(sab, offset);
|
|
worker2.postMessage({sab: sab, offset: offset}, [sab]);
|
|
|
|
// Spin until the worker is waiting on the futex.
|
|
while (%AtomicsFutexNumWaitersForTesting(i32a2, 0) != 1) {}
|
|
Atomics.futexWake(i32a2, 0, 1);
|
|
assertEquals(Atomics.OK, worker2.getMessage());
|
|
worker2.terminate();
|
|
|
|
// Futex should work when index and buffer views are different, but
|
|
// the real address is the same.
|
|
var worker3 = new Worker(workerScript);
|
|
i32a2 = new Int32Array(sab, 4);
|
|
worker3.postMessage({sab: sab, offset: 8}, [sab]);
|
|
|
|
// Spin until the worker is waiting on the futex.
|
|
while (%AtomicsFutexNumWaitersForTesting(i32a2, 1) != 1) {}
|
|
Atomics.futexWake(i32a2, 1, 1);
|
|
assertEquals(Atomics.OK, worker3.getMessage());
|
|
worker3.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());
|
|
}
|
|
|
|
// Test futexWakeOrRequeue on offset typed array
|
|
var offset = 16;
|
|
sab = new SharedArrayBuffer(24);
|
|
i32a = new Int32Array(sab);
|
|
var i32a2 = new Int32Array(sab, offset);
|
|
|
|
for (id = 0; id < 4; id++) {
|
|
workers[id].postMessage({sab: sab, id: id}, [sab]);
|
|
}
|
|
|
|
while (%AtomicsFutexNumWaitersForTesting(i32a2, 0) != 4) { }
|
|
|
|
index1 = 0;
|
|
index2 = 1;
|
|
assertEquals(4, %AtomicsFutexNumWaitersForTesting(i32a2, index1));
|
|
assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a2, index2));
|
|
|
|
assertEquals(2, Atomics.futexWakeOrRequeue(i32a2, index1, 2, 0, index2));
|
|
assertEquals(2, %AtomicsFutexNumWaitersForTesting(i32a2, index2));
|
|
assertEquals(0, %AtomicsFutexNumWaitersForTesting(i32a2, index1));
|
|
|
|
assertEquals(2, Atomics.futexWake(i32a2, index2, 2));
|
|
|
|
for (id = 0; id < 4; ++id) {
|
|
assertEquals(Atomics.OK, workers[id].getMessage());
|
|
workers[id].terminate();
|
|
}
|
|
|
|
})();
|
|
|
|
}
|