#![cfg(not(miri))] 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(()) }