Files
wasmtime/tests/all/wait_notify.rs
Harald Hoyer c74706aa59 feat: implement memory.atomic.notify,wait32,wait64 (#5255)
* feat: implement memory.atomic.notify,wait32,wait64

Added the parking_spot crate, which provides the needed registry for the
operations.

Signed-off-by: Harald Hoyer <harald@profian.com>

* fix: change trap message for HeapMisaligned

The threads spec test wants "unaligned atomic"
instead of "misaligned memory access".

Signed-off-by: Harald Hoyer <harald@profian.com>

* tests: add test for atomic wait on non-shared memory

Signed-off-by: Harald Hoyer <harald@profian.com>

* tests: add tests/spec_testsuite/proposals/threads

without pooling and reference types.
Also "shared_memory" is added to the "spectest" interface.

Signed-off-by: Harald Hoyer <harald@profian.com>

* tests: add atomics_notify.wast

checking that notify with 0 waiters returns 0 on shared and non-shared
memory.

Signed-off-by: Harald Hoyer <harald@profian.com>

* tests: add tests for atomic wait on shared memory

- return 2 - timeout for 0
- return 2 - timeout for 1000ns
- return 1 - invalid value

Signed-off-by: Harald Hoyer <harald@profian.com>

* fixup! feat: implement memory.atomic.notify,wait32,wait64

Signed-off-by: Harald Hoyer <harald@profian.com>

* fixup! feat: implement memory.atomic.notify,wait32,wait64

Signed-off-by: Harald Hoyer <harald@profian.com>

Signed-off-by: Harald Hoyer <harald@profian.com>
2022-11-21 18:23:06 +00:00

121 lines
3.8 KiB
Rust

use anyhow::Result;
use std::time::Instant;
use wasmtime::*;
#[test]
fn atomic_wait_timeout_length() -> Result<()> {
let sleep_nanoseconds = 500000000;
let wat = format!(
r#"(module
(import "env" "memory" (memory 1 1 shared))
(func (export "func1") (result i32)
(memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const {sleep_nanoseconds}))
)
(data (i32.const 0) "\00\00\00\00")
)"#
);
let mut config = Config::new();
config.wasm_threads(true);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, wat)?;
let mut store = Store::new(&engine, ());
let shared_memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?;
let instance = Instance::new(&mut store, &module, &[shared_memory.clone().into()])?;
let now = Instant::now();
let func_ret = instance
.get_typed_func::<(), i32>(&mut store, "func1")
.unwrap()
.call(&mut store, ())
.unwrap();
let duration = now.elapsed();
assert!(
duration.as_nanos() >= sleep_nanoseconds,
"duration: {duration:?} < {sleep_nanoseconds:?}"
);
assert_eq!(func_ret, 2);
Ok(())
}
#[test]
fn atomic_wait_notify_basic() -> Result<()> {
let wat = r#"(module
(import "env" "memory" (memory 1 1 shared))
(func (export "first_thread") (result i32)
(drop (memory.atomic.wait32 (i32.const 4) (i32.const 0) (i64.const -1)))
(i32.atomic.store (i32.const 0) (i32.const 42))
(drop (memory.atomic.notify (i32.const 0) (i32.const -1)))
(i32.atomic.load (i32.const 0))
)
(func (export "second_thread") (result i32)
(i32.atomic.store (i32.const 4) (i32.const 21))
(drop (memory.atomic.notify (i32.const 4) (i32.const -1)))
(drop (memory.atomic.wait32 (i32.const 0) (i32.const 0) (i64.const -1)))
(i32.atomic.load (i32.const 0))
)
(data (i32.const 0) "\00\00\00\00")
(data (i32.const 4) "\00\00\00\00")
)"#;
let mut config = Config::new();
config.wasm_threads(true);
let engine = Engine::new(&config)?;
let module = Module::new(&engine, wat)?;
let mut store = Store::new(&engine, ());
let shared_memory = SharedMemory::new(&engine, MemoryType::shared(1, 1))?;
let instance1 = Instance::new(&mut store, &module, &[shared_memory.clone().into()])?;
let thread = {
let engine = engine.clone();
let module = module.clone();
let shared_memory = shared_memory.clone();
std::thread::spawn(move || {
let mut store = Store::new(&engine, ());
let instance2 = Instance::new(&mut store, &module, &[shared_memory.into()]).unwrap();
let instance2_first_word = instance2
.get_typed_func::<(), i32>(&mut store, "second_thread")
.unwrap()
.call(&mut store, ())
.unwrap();
assert_eq!(instance2_first_word, 42);
})
};
let instance1_first_word = instance1
.get_typed_func::<(), i32>(&mut store, "first_thread")
.unwrap()
.call(&mut store, ())
.unwrap();
assert_eq!(instance1_first_word, 42);
thread.join().unwrap();
let data = shared_memory.data();
// Verify that the memory is the same in all shared locations.
let shared_memory_first_word = i32::from_le_bytes(unsafe {
[
*data[0].get(),
*data[1].get(),
*data[2].get(),
*data[3].get(),
]
});
assert_eq!(shared_memory_first_word, 42);
let shared_memory_second_word = i32::from_le_bytes(unsafe {
[
*data[4].get(),
*data[5].get(),
*data[6].get(),
*data[7].get(),
]
});
assert_eq!(shared_memory_second_word, 21);
Ok(())
}