diff --git a/crates/wasmtime/src/engine.rs b/crates/wasmtime/src/engine.rs index 5575e8c12b3a..281ecc661284 100644 --- a/crates/wasmtime/src/engine.rs +++ b/crates/wasmtime/src/engine.rs @@ -154,9 +154,14 @@ impl Engine { #[cfg(feature = "parallel-compilation")] { use rayon::prelude::*; + // If we collect into Result, E> directly, the returned error is not + // deterministic, because any error could be returned early. So we first materialize + // all results in order and then return the first error deterministically, or Ok(_). return input .into_par_iter() .map(|a| f(a)) + .collect::>>() + .into_iter() .collect::, E>>(); } } diff --git a/tests/all/module.rs b/tests/all/module.rs index 7dc7cc9ccd9d..683a93fae5b7 100644 --- a/tests/all/module.rs +++ b/tests/all/module.rs @@ -534,3 +534,27 @@ fn concurrent_type_modifications_and_checks(config: &mut Config) -> Result<()> { Ok(()) } + +#[test] +#[cfg_attr(miri, ignore)] +fn validate_deterministic() { + let mut faulty_wat = "(module ".to_string(); + for i in 0..100 { + faulty_wat.push_str(&format!( + "(func (export \"foo_{i}\") (result i64) (i64.add (i32.const 0) (i64.const 1)))" + )); + } + faulty_wat.push_str(")"); + let binary = wat::parse_str(faulty_wat).unwrap(); + + let engine_parallel = Engine::new(&Config::new().parallel_compilation(true)).unwrap(); + let result_parallel = Module::validate(&engine_parallel, &binary) + .unwrap_err() + .to_string(); + + let engine_sequential = Engine::new(&Config::new().parallel_compilation(false)).unwrap(); + let result_sequential = Module::validate(&engine_sequential, &binary) + .unwrap_err() + .to_string(); + assert_eq!(result_parallel, result_sequential); +}