From 17ca624d914fbac436fec89153b1306f6b6abf62 Mon Sep 17 00:00:00 2001 From: Clemens Backes Date: Tue, 17 Mar 2026 14:57:10 +0100 Subject: [PATCH] [threads][test][js-api] Add tests for wait/notify Add JS API tests for Atomics.wait and Atomics.notify on WebAssembly.Memory. - On shared memory: Atomics.notify should return the number of notified waiters, and Atomics.wait should return the appropriate wait status. - On unshared memory: Atomics.notify should return 0, while Atomics.wait should trap (throwing a WebAssembly.RuntimeError). --- test/js-api/memory/wait-notify-shared.any.js | 61 +++++++++++++++++++ .../js-api/memory/wait-notify-unshared.any.js | 34 +++++++++++ 2 files changed, 95 insertions(+) create mode 100644 test/js-api/memory/wait-notify-shared.any.js create mode 100644 test/js-api/memory/wait-notify-unshared.any.js diff --git a/test/js-api/memory/wait-notify-shared.any.js b/test/js-api/memory/wait-notify-shared.any.js new file mode 100644 index 000000000..9444ebc38 --- /dev/null +++ b/test/js-api/memory/wait-notify-shared.any.js @@ -0,0 +1,61 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +function createModule() { + const builder = new WasmModuleBuilder(); + // Import memory instead of creating a local one. + builder.addImportedMemory('env', 'memory', 1, 1, true); // shared + builder.exportMemoryAs('memory'); + + const kSig_i_iil = makeSig([kWasmI32, kWasmI32, kWasmI64], [kWasmI32]); + builder.addFunction('wait', kSig_i_iil) + .addBody([ + kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2, + kAtomicPrefix, kExprI32AtomicWait, 2, 0 + ]) + .exportFunc(); + + builder.addFunction('notify', kSig_i_ii) + .addBody([ + kExprLocalGet, 0, kExprLocalGet, 1, + kAtomicPrefix, kExprAtomicNotify, 2, 0 + ]) + .exportFunc(); + + return builder.toModule(); +} + +function createInstance(module, memory) { + if (!memory) { + memory = new WebAssembly.Memory({initial: 1, maximum: 1, shared: true}); + } + return new WebAssembly.Instance(module, {env: {memory: memory}}); +} + +const module = createModule(); + +test(() => { + const instance = createInstance(module); + const buffer = new Int32Array(instance.exports.memory.buffer); + buffer[0] = 0; + + const result = instance.exports.wait(0, 1, -1n); + assert_equals( + result, 1, 'Wait32 should return 1 (not-equal) if value doesn\'t match'); +}, 'Wait32 (not-equal) on shared memory'); + +test(() => { + const instance = createInstance(module); + const buffer = new Int32Array(instance.exports.memory.buffer); + buffer[0] = 0; + + const result = instance.exports.wait(0, 0, 1000000n); // 1ms timeout + assert_equals(result, 2, 'Wait32 should return 2 (timed-out) after timeout'); +}, 'Wait32 (timed-out) on shared memory'); + +test(() => { + const instance = createInstance(module); + const result = instance.exports.notify(0, 1); + assert_equals( + result, 0, 'Notify should return 0 (number of waiters notified)'); +}, 'Notify on shared memory (0 waiters)'); diff --git a/test/js-api/memory/wait-notify-unshared.any.js b/test/js-api/memory/wait-notify-unshared.any.js new file mode 100644 index 000000000..cb354fd4c --- /dev/null +++ b/test/js-api/memory/wait-notify-unshared.any.js @@ -0,0 +1,34 @@ +// META: global=window,dedicatedworker,jsshell +// META: script=/wasm/jsapi/wasm-module-builder.js + +test(() => { + const builder = new WasmModuleBuilder(); + builder.addMemory(1, 1, false, false); + builder.addFunction('notify', kSig_i_ii) + .addBody([ + kExprLocalGet, 0, kExprLocalGet, 1, kAtomicPrefix, kExprAtomicNotify, 2, + 0 + ]) + .exportFunc(); + const instance = builder.instantiate(); + const result = instance.exports.notify(0, 1); + assert_equals(result, 0, 'Notify on unshared memory should return 0'); +}, 'Notify on unshared memory'); + +test(() => { + const builder = new WasmModuleBuilder(); + builder.addMemory(1, 1, false, false); + const kSig_i_iil = makeSig([kWasmI32, kWasmI32, kWasmI64], [kWasmI32]); + builder.addFunction('wait', kSig_i_iil) + .addBody([ + kExprLocalGet, 0, kExprLocalGet, 1, kExprLocalGet, 2, kAtomicPrefix, + kExprI32AtomicWait, 2, 0 + ]) + .exportFunc(); + const instance = builder.instantiate(); + // This should trap. We use a non-infinite timeout to avoid hanging if the + // trap is not implemented. + assert_throws_js( + WebAssembly.RuntimeError, () => instance.exports.wait(0, 0, 1000n), + 'Wait on unshared memory should trap'); +}, 'Wait on unshared memory traps');