87afe62bf0
Bug: chromium:1190951 Change-Id: I2c314a143c77a9fee288f7822fea84f900c3059b Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2921033 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Deepti Gandluri <gdeepti@chromium.org> Cr-Commit-Position: refs/heads/master@{#74930}
361 lines
13 KiB
JavaScript
361 lines
13 KiB
JavaScript
// Copyright 2018 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
|
|
// Flags: --experimental-wasm-threads
|
|
|
|
'use strict';
|
|
|
|
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|
|
|
function WasmAtomicNotify(memory, offset, index, num) {
|
|
let builder = new WasmModuleBuilder();
|
|
builder.addImportedMemory("m", "memory", 0, 20, "shared");
|
|
builder.addFunction("main", kSig_i_ii)
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprLocalGet, 1,
|
|
kAtomicPrefix,
|
|
kExprAtomicNotify, /* alignment */ 0, offset])
|
|
.exportAs("main");
|
|
|
|
// Instantiate module, get function exports
|
|
let module = new WebAssembly.Module(builder.toBuffer());
|
|
let instance = new WebAssembly.Instance(module, {m: {memory}});
|
|
return instance.exports.main(index, num);
|
|
}
|
|
|
|
function WasmI32AtomicWait(memory, offset, index, val, timeout) {
|
|
let builder = new WasmModuleBuilder();
|
|
builder.addImportedMemory("m", "memory", 0, 20, "shared");
|
|
builder.addFunction("main",
|
|
makeSig([kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
|
|
.addBody([
|
|
kExprLocalGet, 0,
|
|
kExprLocalGet, 1,
|
|
kExprLocalGet, 2,
|
|
kExprI64SConvertF64,
|
|
kAtomicPrefix,
|
|
kExprI32AtomicWait, /* alignment */ 0, offset])
|
|
.exportAs("main");
|
|
|
|
// Instantiate module, get function exports
|
|
let module = new WebAssembly.Module(builder.toBuffer());
|
|
let instance = new WebAssembly.Instance(module, {m: {memory}});
|
|
return instance.exports.main(index, val, timeout);
|
|
}
|
|
|
|
function WasmI64AtomicWait(memory, offset, index, val_low,
|
|
val_high, timeout) {
|
|
let builder = new WasmModuleBuilder();
|
|
builder.addImportedMemory("m", "memory", 0, 20, "shared");
|
|
// Wrapper for I64AtomicWait that takes two I32 values and combines to into
|
|
// I64 for the instruction parameter.
|
|
builder.addFunction("main",
|
|
makeSig([kWasmI32, kWasmI32, kWasmI32, kWasmF64], [kWasmI32]))
|
|
.addLocals(kWasmI64, 1) // local that is passed as value param to wait
|
|
.addBody([
|
|
kExprLocalGet, 1,
|
|
kExprI64UConvertI32,
|
|
kExprI64Const, 32,
|
|
kExprI64Shl,
|
|
kExprLocalGet, 2,
|
|
kExprI64UConvertI32,
|
|
kExprI64Ior,
|
|
kExprLocalSet, 4, // Store the created I64 value in local
|
|
kExprLocalGet, 0,
|
|
kExprLocalGet, 4,
|
|
kExprLocalGet, 3,
|
|
kExprI64SConvertF64,
|
|
kAtomicPrefix,
|
|
kExprI64AtomicWait, /* alignment */ 0, offset])
|
|
.exportAs("main");
|
|
|
|
// Instantiate module, get function exports
|
|
let module = new WebAssembly.Module(builder.toBuffer());
|
|
let instance = new WebAssembly.Instance(module, {m: {memory}});
|
|
return instance.exports.main(index, val_high, val_low, timeout);
|
|
}
|
|
|
|
(function TestInvalidIndex() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
|
|
// Valid indexes are 0-65535 (1 page).
|
|
[-2, 65536, 0xffffffff].forEach(function(invalidIndex) {
|
|
assertThrows(function() {
|
|
WasmAtomicNotify(memory, 0, invalidIndex, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, 0, invalidIndex, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, 0, invalidIndex, 0, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmAtomicNotify(memory, invalidIndex, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, invalidIndex, 0, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, invalidIndex, 0, 0, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmAtomicNotify(memory, invalidIndex/2, invalidIndex/2, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, invalidIndex/2, invalidIndex/2, 0, -1);
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, invalidIndex/2, invalidIndex/2, 0, 0, -1);
|
|
}, Error);
|
|
});
|
|
})();
|
|
|
|
(function TestInvalidAlignment() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
|
|
// Wait and wake must be 4 byte aligned.
|
|
[1, 2, 3].forEach(function(invalid) {
|
|
assertThrows(function() {
|
|
WasmAtomicNotify(memory, invalid, 0, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmAtomicNotify(memory, 0, invalid, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, invalid, 0, 0, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, 0, invalid, 0, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, invalid, 0, 0, 0, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, 0, invalid, 0, 0, -1)
|
|
}, Error);
|
|
});
|
|
|
|
//WasmI64AtomicWait must be 8 byte aligned.
|
|
[4, 5, 6, 7].forEach(function(invalid) {
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, 0, invalid, 0, 0, -1)
|
|
}, Error);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, invalid, 0, 0, 0, -1)
|
|
}, Error);
|
|
});
|
|
})();
|
|
|
|
(function TestI32WaitTimeout() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
var waitMs = 100;
|
|
var startTime = new Date();
|
|
assertEquals(2, WasmI32AtomicWait(memory, 0, 0, 0, waitMs*1000000));
|
|
var endTime = new Date();
|
|
assertTrue(endTime - startTime >= waitMs);
|
|
})();
|
|
|
|
(function TestI64WaitTimeout() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
var waitMs = 100;
|
|
var startTime = new Date();
|
|
assertEquals(2, WasmI64AtomicWait(memory, 0, 0, 0, 0, waitMs*1000000));
|
|
var endTime = new Date();
|
|
assertTrue(endTime - startTime >= waitMs);
|
|
})();
|
|
|
|
(function TestI32WaitNotEqual() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
assertEquals(1, WasmI32AtomicWait(memory, 0, 0, 42, -1));
|
|
|
|
assertEquals(2, WasmI32AtomicWait(memory, 0, 0, 0, 0));
|
|
|
|
let i32a = new Int32Array(memory.buffer);
|
|
i32a[0] = 1;
|
|
assertEquals(1, WasmI32AtomicWait(memory, 0, 0, 0, -1));
|
|
assertEquals(2, WasmI32AtomicWait(memory, 0, 0, 1, 0));
|
|
})();
|
|
|
|
(function TestI64WaitNotEqual() {
|
|
if (!%IsAtomicsWaitAllowed()) return;
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
assertEquals(1, WasmI64AtomicWait(memory, 0, 0, 42, 0, -1));
|
|
|
|
assertEquals(2, WasmI64AtomicWait(memory, 0, 0, 0, 0, 0));
|
|
|
|
let i32a = new Int32Array(memory.buffer);
|
|
i32a[0] = 1;
|
|
i32a[1] = 2;
|
|
assertEquals(1, WasmI64AtomicWait(memory, 0, 0, 0, 0, -1));
|
|
assertEquals(2, WasmI64AtomicWait(memory, 0, 0, 1, 2, 0));
|
|
})();
|
|
|
|
(function TestWakeCounts() {
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
|
|
[-1, 0, 4, 100, 0xffffffff].forEach(function(count) {
|
|
WasmAtomicNotify(memory, 0, 0, count);
|
|
});
|
|
})();
|
|
|
|
//// WORKER ONLY TESTS
|
|
|
|
if (this.Worker) {
|
|
|
|
// This test creates 4 workers that wait on consecutive (8 byte separated to
|
|
// satisfy alignments for all kinds of wait) memory locations to test various
|
|
// wait/wake combinations. For each combination, each thread waits 3 times
|
|
// expecting all 4 threads to be woken with wake(4) in first iteration, all 4
|
|
// to be woken with wake(5) in second iteration and, 3 and 1 to be woken in
|
|
// third iteration.
|
|
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
let i32a = new Int32Array(memory.buffer);
|
|
const numWorkers = 4;
|
|
|
|
let workerScript = `onmessage = function(msg) {
|
|
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
|
|
${WasmI32AtomicWait.toString()}
|
|
${WasmI64AtomicWait.toString()}
|
|
let id = msg.id;
|
|
let memory = msg.memory;
|
|
let i32a = new Int32Array(memory.buffer);
|
|
// indices are right shifted by 2 for Atomics.wait to convert them to index
|
|
// for Int32Array
|
|
// for wasm-wake numWorkers threads
|
|
let result = Atomics.wait(i32a, 0>>>2, 0);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers + 1 threads
|
|
result = Atomics.wait(i32a, 8>>>2, 0);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers - 1 threads
|
|
result = Atomics.wait(i32a, 16>>>2, 0);
|
|
postMessage(result);
|
|
// for js-wake numWorkers threads
|
|
result = WasmI32AtomicWait(memory, 0, 24, 0, -1);
|
|
postMessage(result);
|
|
// for js-wake numWorkers + 1 threads
|
|
result = WasmI32AtomicWait(memory, 0, 32, 0, -1);
|
|
postMessage(result);
|
|
// for js-wake numWorkers - 1 threads
|
|
result = WasmI32AtomicWait(memory, 0, 40, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers threads
|
|
result = WasmI32AtomicWait(memory, 0, 48, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers + 1 threads
|
|
result = WasmI32AtomicWait(memory, 0, 56, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers - 1 threads
|
|
result = WasmI32AtomicWait(memory, 0, 64, 0, -1);
|
|
postMessage(result);
|
|
// for js-wake numWorkers threads
|
|
result = WasmI64AtomicWait(memory, 0, 72, 0, 0, -1);
|
|
postMessage(result);
|
|
// for js-wake numWorkers + 1 threads
|
|
result = WasmI64AtomicWait(memory, 0, 80, 0, 0, -1);
|
|
postMessage(result);
|
|
// for js-wake numWorkers - 1 threads
|
|
result = WasmI64AtomicWait(memory, 0, 88, 0, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers threads
|
|
result = WasmI64AtomicWait(memory, 0, 96, 0, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers + 1 threads
|
|
result = WasmI64AtomicWait(memory, 0, 104, 0, 0, -1);
|
|
postMessage(result);
|
|
// for wasm-wake numWorkers - 1 threads
|
|
result = WasmI64AtomicWait(memory, 0, 112, 0, 0, -1);
|
|
postMessage(result);
|
|
};`;
|
|
|
|
let waitForAllWorkers = function(index) {
|
|
// index is right shifted by 2 to convert to index in Int32Array
|
|
while (%AtomicsNumWaitersForTesting(i32a, index>>>2) != numWorkers) {}
|
|
}
|
|
|
|
let jsWakeCheck = function(index, num, workers, msg) {
|
|
waitForAllWorkers(index);
|
|
let indexJs = index>>>2; // convert to index in Int32Array
|
|
if (num >= numWorkers) {
|
|
// if numWorkers or more is passed to wake, numWorkers workers should be
|
|
// woken.
|
|
assertEquals(numWorkers, Atomics.notify(i32a, indexJs, num));
|
|
} else {
|
|
// if num < numWorkers is passed to wake, num workers should be woken.
|
|
// Then the remaining workers are woken for the next part
|
|
assertEquals(num, Atomics.notify(i32a, indexJs, num));
|
|
assertEquals(numWorkers-num, Atomics.notify(i32a, indexJs, numWorkers));
|
|
}
|
|
for (let id = 0; id < numWorkers; id++) {
|
|
assertEquals(msg, workers[id].getMessage());
|
|
}
|
|
};
|
|
|
|
let wasmWakeCheck = function(index, num, workers, msg) {
|
|
waitForAllWorkers(index);
|
|
if (num >= numWorkers) {
|
|
// if numWorkers or more is passed to wake, numWorkers workers should be
|
|
// woken.
|
|
assertEquals(numWorkers, WasmAtomicNotify(memory, 0, index, num));
|
|
} else {
|
|
// if num < numWorkers is passed to wake, num workers should be woken.
|
|
// Then the remaining workers are woken for the next part
|
|
assertEquals(num, WasmAtomicNotify(memory, 0, index, num));
|
|
assertEquals(numWorkers-num,
|
|
WasmAtomicNotify(memory, 0, index, numWorkers));
|
|
}
|
|
for (let id = 0; id < numWorkers; id++) {
|
|
assertEquals(msg, workers[id].getMessage());
|
|
}
|
|
};
|
|
|
|
let workers = [];
|
|
for (let id = 0; id < numWorkers; id++) {
|
|
workers[id] = new Worker(workerScript, {type: 'string'});
|
|
workers[id].postMessage({id, memory});
|
|
}
|
|
|
|
wasmWakeCheck(0, numWorkers, workers, "ok");
|
|
wasmWakeCheck(8, numWorkers + 1, workers, "ok");
|
|
wasmWakeCheck(16, numWorkers - 1, workers, "ok");
|
|
|
|
jsWakeCheck(24, numWorkers, workers, 0);
|
|
jsWakeCheck(32, numWorkers + 1, workers, 0);
|
|
jsWakeCheck(40, numWorkers - 1, workers, 0);
|
|
|
|
wasmWakeCheck(48, numWorkers, workers, 0);
|
|
wasmWakeCheck(56, numWorkers + 1, workers, 0);
|
|
wasmWakeCheck(64, numWorkers - 1, workers, 0);
|
|
|
|
jsWakeCheck(72, numWorkers, workers, 0);
|
|
jsWakeCheck(80, numWorkers + 1, workers, 0);
|
|
jsWakeCheck(88, numWorkers - 1, workers, 0);
|
|
|
|
wasmWakeCheck(96, numWorkers, workers, 0);
|
|
wasmWakeCheck(104, numWorkers + 1, workers, 0);
|
|
wasmWakeCheck(112, numWorkers - 1, workers, 0);
|
|
|
|
for (let id = 0; id < numWorkers; id++) {
|
|
workers[id].terminate();
|
|
}
|
|
}
|
|
|
|
(function TestWaitTrapsOnDisallowedIsolate() {
|
|
let memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true});
|
|
var waitMs = 100;
|
|
%SetAllowAtomicsWait(false)
|
|
assertThrows(function() {
|
|
WasmI32AtomicWait(memory, 0, 0, 0, waitMs*1000000)}, WebAssembly.RuntimeError);
|
|
assertThrows(function() {
|
|
WasmI64AtomicWait(memory, 0, 0, 0, waitMs*1000000)}, WebAssembly.RuntimeError);
|
|
})();
|