()
| 196 | #[test] |
| 197 | #[cfg_attr(miri, ignore)] |
| 198 | fn test_grow_memory_in_multiple_threads() -> Result<()> { |
| 199 | const NUM_THREADS: usize = 4; |
| 200 | const NUM_GROW_OPS: usize = 1000; |
| 201 | |
| 202 | let wat = r#"(module |
| 203 | (import "env" "memory" (memory 1 4000 shared)) |
| 204 | (func (export "grow") (param $delta i32) (result i32) (memory.grow (local.get $delta))) |
| 205 | )"#; |
| 206 | |
| 207 | let Some(engine) = engine() else { |
| 208 | return Ok(()); |
| 209 | }; |
| 210 | let module = Module::new(&engine, wat)?; |
| 211 | let shared_memory = SharedMemory::new(&engine, MemoryType::shared(1, NUM_GROW_OPS as u32))?; |
| 212 | let mut threads = vec![]; |
| 213 | let observed_sizes = Arc::new(RwLock::new(vec![])); |
| 214 | |
| 215 | // Spawn several threads using a single shared memory and grow the memory |
| 216 | // concurrently on all threads. |
| 217 | for _ in 0..NUM_THREADS { |
| 218 | let engine = engine.clone(); |
| 219 | let module = module.clone(); |
| 220 | let observed_sizes = observed_sizes.clone(); |
| 221 | let shared_memory = shared_memory.clone(); |
| 222 | let thread = std::thread::spawn(move || { |
| 223 | let mut store = Store::new(&engine, ()); |
| 224 | let instance = Instance::new(&mut store, &module, &[shared_memory.into()]).unwrap(); |
| 225 | let grow_fn = instance |
| 226 | .get_typed_func::<i32, i32>(&mut store, "grow") |
| 227 | .unwrap(); |
| 228 | let mut thread_local_observed_sizes: Vec<_> = (0..NUM_GROW_OPS / NUM_THREADS) |
| 229 | .map(|_| grow_fn.call(&mut store, 1).unwrap() as u32) |
| 230 | .collect(); |
| 231 | println!( |
| 232 | "Returned memory sizes for {:?}: {:?}", |
| 233 | std::thread::current().id(), |
| 234 | thread_local_observed_sizes |
| 235 | ); |
| 236 | assert!(is_sorted(thread_local_observed_sizes.as_slice())); |
| 237 | observed_sizes |
| 238 | .write() |
| 239 | .unwrap() |
| 240 | .append(&mut thread_local_observed_sizes); |
| 241 | }); |
| 242 | threads.push(thread); |
| 243 | } |
| 244 | |
| 245 | // Wait for all threads to finish. |
| 246 | for t in threads { |
| 247 | t.join().unwrap() |
| 248 | } |
| 249 | |
| 250 | // Ensure the returned "old memory sizes" are all unique--i.e., we have not |
| 251 | // observed the same growth twice. |
| 252 | let unique_observed_sizes: HashSet<u32, RandomState> = |
| 253 | HashSet::from_iter(observed_sizes.read().unwrap().iter().cloned()); |
| 254 | assert_eq!( |
| 255 | observed_sizes.read().unwrap().len(), |
nothing calls this directly
no test coverage detected