Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit 33062f2

Browse files
committed
fix issues running component-async-tests under miri
In my previous commit, I had missed making a few test suites miri-compatible. In addition, some of the suites (e.g. `round_trip`) run each test multiple times using different host APIs, which is intolerably slow on miri, so we now only use one call style per test when miri is enabled. Relatedly, we now build the test programs using `--release` when the `MIRI_TEST_CWASM_DIR` environment variable is set. Finally, miri's willingness to allocate `[u8]` arrays which are _not_ 4-byte aligned exposed an issue in `error_context_new`, which I've fixed. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
1 parent d5a2987 commit 33062f2

20 files changed

Lines changed: 436 additions & 290 deletions

File tree

crates/misc/component-async-tests/src/round_trip.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub mod non_concurrent_export_bindings {
2525

2626
impl bindings::local::local::baz::HostConcurrent for Ctx {
2727
async fn foo<T>(_: &mut Accessor<T, Self>, s: String) -> wasmtime::Result<String> {
28-
tokio::time::sleep(Duration::from_millis(10)).await;
28+
crate::util::sleep(Duration::from_millis(10)).await;
2929
Ok(format!("{s} - entered host - exited host"))
3030
}
3131
}

crates/misc/component-async-tests/src/round_trip_direct.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub mod bindings {
1515

1616
impl bindings::RoundTripDirectImportsConcurrent for Ctx {
1717
async fn foo<T>(_: &mut Accessor<T, Self>, s: String) -> wasmtime::Result<String> {
18-
tokio::time::sleep(Duration::from_millis(10)).await;
18+
crate::util::sleep(Duration::from_millis(10)).await;
1919
Ok(format!("{s} - entered host - exited host"))
2020
}
2121
}

crates/misc/component-async-tests/src/round_trip_many.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl bindings::local::local::many::HostConcurrent for Ctx {
4747
Option<Stuff>,
4848
Result<Stuff, ()>,
4949
)> {
50-
tokio::time::sleep(Duration::from_millis(10)).await;
50+
crate::util::sleep(Duration::from_millis(10)).await;
5151
Ok((
5252
format!("{a} - entered host - exited host"),
5353
b,

crates/misc/component-async-tests/src/sleep.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ wasmtime::component::bindgen!({
1616

1717
impl local::local::sleep::HostConcurrent for Ctx {
1818
async fn sleep_millis<T>(_: &mut Accessor<T, Self>, time_in_millis: u64) {
19-
tokio::time::sleep(Duration::from_millis(time_in_millis)).await;
19+
crate::util::sleep(Duration::from_millis(time_in_millis)).await;
2020
}
2121
}
2222

crates/misc/component-async-tests/src/util.rs

Lines changed: 67 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,60 @@ pub fn config() -> Config {
4242
config
4343
}
4444

45+
pub async fn sleep(duration: std::time::Duration) {
46+
if cfg!(miri) {
47+
// TODO: We should be able to use `tokio::time::sleep` here, but as of
48+
// this writing the miri-compatible version of `wasmtime-fiber` uses
49+
// threads behind the scenes, which means thread-local storage is not
50+
// preserved when we switch fibers, and that confuses Tokio. If we ever
51+
// fix that we can stop using our own, special version of `sleep` and
52+
// switch back to the Tokio version.
53+
54+
use std::{
55+
future,
56+
sync::{
57+
Arc, Mutex,
58+
atomic::{AtomicU32, Ordering::SeqCst},
59+
},
60+
task::Poll,
61+
thread,
62+
};
63+
64+
let state = Arc::new(AtomicU32::new(0));
65+
let waker = Arc::new(Mutex::new(None));
66+
let mut join_handle = None;
67+
future::poll_fn(move |cx| match state.load(SeqCst) {
68+
0 => {
69+
state.store(1, SeqCst);
70+
let state = state.clone();
71+
*waker.lock().unwrap() = Some(cx.waker().clone());
72+
let waker = waker.clone();
73+
join_handle = Some(thread::spawn(move || {
74+
thread::sleep(duration);
75+
state.store(2, SeqCst);
76+
let waker = waker.lock().unwrap().clone().unwrap();
77+
waker.wake();
78+
}));
79+
Poll::Pending
80+
}
81+
1 => {
82+
*waker.lock().unwrap() = Some(cx.waker().clone());
83+
Poll::Pending
84+
}
85+
2 => {
86+
if let Some(handle) = join_handle.take() {
87+
_ = handle.join();
88+
}
89+
Poll::Ready(())
90+
}
91+
_ => unreachable!(),
92+
})
93+
.await;
94+
} else {
95+
tokio::time::sleep(duration).await;
96+
}
97+
}
98+
4599
/// Compose two components
46100
///
47101
/// a is the "root" component, and b is composed into it
@@ -166,7 +220,11 @@ pub async fn test_run(components: &[&str]) -> Result<()> {
166220

167221
pub async fn test_run_with_count(components: &[&str], count: usize) -> Result<()> {
168222
let mut config = config();
169-
config.epoch_interruption(true);
223+
// As of this writing, miri/pulley/epochs is a problematic combination, so
224+
// we don't test it.
225+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
226+
config.epoch_interruption(true);
227+
}
170228

171229
let engine = Engine::new(&config)?;
172230

@@ -198,12 +256,15 @@ pub async fn test_run_with_count(components: &[&str], count: usize) -> Result<()
198256
wakers: Arc::new(std::sync::Mutex::new(None)),
199257
},
200258
);
201-
store.set_epoch_deadline(1);
202259

203-
std::thread::spawn(move || {
204-
std::thread::sleep(Duration::from_secs(10));
205-
engine.increment_epoch();
206-
});
260+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
261+
store.set_epoch_deadline(1);
262+
263+
std::thread::spawn(move || {
264+
std::thread::sleep(Duration::from_secs(10));
265+
engine.increment_epoch();
266+
});
267+
}
207268

208269
let instance = linker.instantiate_async(&mut store, &component).await?;
209270
let yield_host = super::yield_host::bindings::YieldHost::new(&mut store, &instance)?;

crates/misc/component-async-tests/tests/scenario/borrowing.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::env;
12
use std::sync::{Arc, Mutex};
23
use std::time::Duration;
34

@@ -65,7 +66,11 @@ pub async fn async_borrowing_callee() -> Result<()> {
6566

6667
pub async fn test_run_bool(components: &[&str], v: bool) -> Result<()> {
6768
let mut config = config();
68-
config.epoch_interruption(true);
69+
// As of this writing, miri/pulley/epochs is a problematic combination, so
70+
// we don't test it.
71+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
72+
config.epoch_interruption(true);
73+
}
6974

7075
let engine = Engine::new(&config)?;
7176

@@ -88,12 +93,15 @@ pub async fn test_run_bool(components: &[&str], v: bool) -> Result<()> {
8893
wakers: Arc::new(Mutex::new(None)),
8994
},
9095
);
91-
store.set_epoch_deadline(1);
9296

93-
std::thread::spawn(move || {
94-
std::thread::sleep(Duration::from_secs(10));
95-
engine.increment_epoch();
96-
});
97+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
98+
store.set_epoch_deadline(1);
99+
100+
std::thread::spawn(move || {
101+
std::thread::sleep(Duration::from_secs(10));
102+
engine.increment_epoch();
103+
});
104+
}
97105

98106
let instance = linker.instantiate_async(&mut store, &component).await?;
99107
let borrowing_host =

crates/misc/component-async-tests/tests/scenario/common.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ pub async fn compose(a: &[u8], b: &[u8]) -> Result<Vec<u8>> {
4949
#[allow(unused)]
5050
pub async fn test_run(component: &[u8]) -> Result<()> {
5151
let mut config = config();
52-
config.epoch_interruption(true);
52+
// As of this writing, miri/pulley/epochs is a problematic combination, so
53+
// we don't test it.
54+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
55+
config.epoch_interruption(true);
56+
}
5357

5458
let engine = Engine::new(&config)?;
5559

@@ -80,12 +84,15 @@ pub async fn test_run(component: &[u8]) -> Result<()> {
8084
wakers: Arc::new(Mutex::new(None)),
8185
},
8286
);
83-
store.set_epoch_deadline(1);
8487

85-
std::thread::spawn(move || {
86-
std::thread::sleep(Duration::from_secs(10));
87-
engine.increment_epoch();
88-
});
88+
if env::var_os("MIRI_TEST_CWASM_DIR").is_none() {
89+
store.set_epoch_deadline(1);
90+
91+
std::thread::spawn(move || {
92+
std::thread::sleep(Duration::from_secs(10));
93+
engine.increment_epoch();
94+
});
95+
}
8996

9097
let yield_host = component_async_tests::yield_host::bindings::YieldHost::instantiate_async(
9198
&mut store, &component, &linker,

crates/misc/component-async-tests/tests/scenario/error_context.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ pub async fn async_error_context() -> Result<()> {
77
test_run(&[test_programs_artifacts::ASYNC_ERROR_CONTEXT_COMPONENT]).await
88
}
99

10-
#[tokio::test]
11-
pub async fn async_error_context_callee() -> Result<()> {
12-
test_run(&[test_programs_artifacts::ASYNC_ERROR_CONTEXT_COMPONENT]).await
13-
}
10+
// No-op function; we only test this by composing it in `async_error_context_caller`
11+
#[allow(
12+
dead_code,
13+
reason = "here only to make the `assert_test_exists` macro happy"
14+
)]
15+
pub fn async_error_context_callee() {}
1416

1517
#[tokio::test]
1618
pub async fn async_error_context_caller() -> Result<()> {

0 commit comments

Comments
 (0)