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

Commit 9b7496b

Browse files
committed
ci: add feature to run test-suite with async suspends
1 parent b49c1c4 commit 9b7496b

File tree

2 files changed

+117
-1
lines changed

2 files changed

+117
-1
lines changed

crates/tinywasm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ logging=["log", "tinywasm-parser?/logging", "tinywasm-types/logging"]
3737
std=["tinywasm-parser?/std", "tinywasm-types/std"]
3838
parser=["dep:tinywasm-parser"]
3939
archive=["tinywasm-types/archive"]
40+
test_async=[] #feels weird putting it here
4041

4142
[[test]]
4243
name="test-wasm-1"

crates/tinywasm/tests/testsuite/util.rs

Lines changed: 116 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
use std::hash::Hasher;
12
use std::panic::{self, AssertUnwindSafe};
23

34
use eyre::{bail, eyre, Result};
5+
use tinywasm::{CoroState, SuspendConditions, SuspendReason};
46
use tinywasm_types::{ExternRef, FuncRef, ModuleInstanceAddr, TinyWasmModule, ValType, WasmValue};
57
use wasm_testsuite::wast;
68
use wasm_testsuite::wast::{core::AbstractHeapType, QuoteWat};
@@ -12,6 +14,25 @@ pub fn try_downcast_panic(panic: Box<dyn std::any::Any + Send>) -> String {
1214
info.unwrap_or(info_str.unwrap_or(&info_string.unwrap_or("unknown panic".to_owned())).to_string())
1315
}
1416

17+
// due to imprecision it's not exact
18+
fn make_sometimes_breaking_cb(probability: f64) -> impl FnMut(&tinywasm::Store) -> std::ops::ControlFlow<(), ()> {
19+
let mut counter = 0 as u64;
20+
let mut hasher = std::hash::DefaultHasher::new();
21+
let threshhold = (probability * (u64::MAX as f64)) as u64; // 2 lossy conversions
22+
23+
move |_| {
24+
hasher.write_u64(counter);
25+
counter += 1;
26+
if hasher.finish() < threshhold {
27+
std::ops::ControlFlow::Break(())
28+
} else {
29+
std::ops::ControlFlow::Continue(())
30+
}
31+
}
32+
}
33+
34+
35+
#[cfg(not(feature = "test_async"))]
1536
pub fn exec_fn_instance(
1637
instance: Option<&ModuleInstanceAddr>,
1738
store: &mut tinywasm::Store,
@@ -30,6 +51,51 @@ pub fn exec_fn_instance(
3051
func.call(store, args)
3152
}
3253

54+
55+
#[cfg(feature = "test_async")]
56+
pub fn exec_fn_instance(
57+
instance: Option<&ModuleInstanceAddr>,
58+
store: &mut tinywasm::Store,
59+
name: &str,
60+
args: &[tinywasm_types::WasmValue],
61+
) -> Result<Vec<tinywasm_types::WasmValue>, tinywasm::Error> {
62+
let Some(instance) = instance else {
63+
return Err(tinywasm::Error::Other("no instance found".to_string()));
64+
};
65+
66+
let mut prev_reason = None;
67+
store.update_suspend_conditions(|old_cond| {
68+
prev_reason = Some(old_cond);
69+
SuspendConditions { suspend_cb: Some(Box::new(make_sometimes_breaking_cb(2.0 / 3.0))), ..Default::default() }
70+
});
71+
let res = || -> Result<Vec<tinywasm_types::WasmValue>, tinywasm::Error> {
72+
let Some(instance) = store.get_module_instance(*instance) else {
73+
return Err(tinywasm::Error::Other("no instance found".to_string()));
74+
};
75+
76+
let func = instance.exported_func_untyped(store, name)?;
77+
let mut state = match func.call_coro(store, args)? {
78+
tinywasm::PotentialCoroCallResult::Return(val) => return Ok(val),
79+
tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, state) => {
80+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback));
81+
state
82+
}
83+
};
84+
loop {
85+
match state.resume(store, None)? {
86+
tinywasm::CoroStateResumeResult::Return(val) => return Ok(val),
87+
tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => {
88+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback))
89+
}
90+
}
91+
}
92+
}();
93+
// restore store suspend conditions before returning error or success
94+
store.set_suspend_conditions(prev_reason.unwrap());
95+
res
96+
}
97+
98+
#[cfg(not(feature = "test_async"))]
3399
pub fn exec_fn(
34100
module: Option<&TinyWasmModule>,
35101
name: &str,
@@ -39,13 +105,62 @@ pub fn exec_fn(
39105
let Some(module) = module else {
40106
return Err(tinywasm::Error::Other("no module found".to_string()));
41107
};
42-
43108
let mut store = tinywasm::Store::new();
44109
let module = tinywasm::Module::from(module);
45110
let instance = module.instantiate(&mut store, imports)?;
46111
instance.exported_func_untyped(&store, name)?.call(&mut store, args)
47112
}
48113

114+
#[cfg(feature = "test_async")]
115+
pub fn exec_fn(
116+
module: Option<&TinyWasmModule>,
117+
name: &str,
118+
args: &[tinywasm_types::WasmValue],
119+
imports: Option<tinywasm::Imports>,
120+
) -> Result<Vec<tinywasm_types::WasmValue>, tinywasm::Error> {
121+
let Some(module) = module else {
122+
return Err(tinywasm::Error::Other("no module found".to_string()));
123+
};
124+
125+
let mut store = tinywasm::Store::new();
126+
127+
store.set_suspend_conditions(SuspendConditions {
128+
suspend_cb: Some(Box::new(make_sometimes_breaking_cb(2.0 / 3.0))),
129+
..Default::default()
130+
});
131+
132+
let module = tinywasm::Module::from(module);
133+
let instance = match module.instantiate_coro(&mut store, imports)? {
134+
tinywasm::PotentialCoroCallResult::Return(res) => res,
135+
tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, mut state) => loop {
136+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback));
137+
match state.resume(&mut store, None)? {
138+
tinywasm::CoroStateResumeResult::Return(res) => break res,
139+
tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => {
140+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback));
141+
}
142+
}
143+
},
144+
};
145+
146+
let mut state = match instance.exported_func_untyped(&store, name)?.call_coro(&mut store, args)? {
147+
tinywasm::PotentialCoroCallResult::Return(r) => return Ok(r),
148+
tinywasm::PotentialCoroCallResult::Suspended(suspend_reason, state) => {
149+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback));
150+
state
151+
}
152+
};
153+
loop {
154+
match state.resume(&mut store, None)? {
155+
tinywasm::CoroStateResumeResult::Return(res) => return Ok(res),
156+
tinywasm::CoroStateResumeResult::Suspended(suspend_reason) => {
157+
assert!(matches!(suspend_reason, SuspendReason::SuspendedCallback))
158+
}
159+
}
160+
}
161+
}
162+
163+
49164
pub fn catch_unwind_silent<R>(f: impl FnOnce() -> R) -> std::thread::Result<R> {
50165
let prev_hook = panic::take_hook();
51166
panic::set_hook(Box::new(|_| {}));

0 commit comments

Comments
 (0)