Path: blob/main/tests/all/epoch_interruption.rs
1690 views
#![cfg(not(miri))]12use crate::async_functions::{CountPending, PollOnce};3use anyhow::anyhow;4use std::sync::Arc;5use std::sync::atomic::{AtomicBool, Ordering};6use wasmtime::*;7use wasmtime_test_macros::wasmtime_test;89fn build_engine(config: &mut Config) -> Result<Arc<Engine>> {10config.async_support(true);11config.epoch_interruption(true);12Ok(Arc::new(Engine::new(&config)?))13}1415fn make_env<T: 'static>(engine: &Engine) -> Linker<T> {16let mut linker = Linker::new(engine);17let engine = engine.clone();1819linker20.func_new(21"",22"bump_epoch",23FuncType::new(&engine, None, None),24move |_caller, _params, _results| {25engine.increment_epoch();26Ok(())27},28)29.unwrap();3031linker32}3334enum InterruptMode {35Trap,36Callback(fn(StoreContextMut<usize>) -> Result<UpdateDeadline>),37Yield(u64),38}3940/// Run a test with the given wasm, giving an initial deadline of41/// `initial` ticks in the future, and either configuring the wasm to42/// yield and set a deadline `delta` ticks in the future if `delta` is43/// `Some(..)` or trapping if `delta` is `None`.44///45/// Returns `Some((yields, store))` if function completed normally, giving46/// the number of yields that occurred, or `None` if a trap occurred.47async fn run_and_count_yields_or_trap<F: Fn(Arc<Engine>)>(48config: &mut Config,49wasm: &str,50initial: u64,51delta: InterruptMode,52setup_func: F,53) -> Result<Option<(usize, usize)>> {54let engine = build_engine(config)?;55let linker = make_env::<usize>(&engine);56let module = Module::new(&engine, wasm)?;57let mut store = Store::new(&engine, 0);58store.set_epoch_deadline(initial);59match delta {60InterruptMode::Yield(delta) => {61store.epoch_deadline_async_yield_and_update(delta);62}63InterruptMode::Callback(func) => {64store.epoch_deadline_callback(func);65}66InterruptMode::Trap => {67store.epoch_deadline_trap();68}69}7071let engine_clone = engine.clone();72setup_func(engine_clone);7374let instance = linker.instantiate_async(&mut store, &module).await?;75let f = instance.get_func(&mut store, "run").unwrap();76let (result, yields) =77CountPending::new(Box::pin(f.call_async(&mut store, &[], &mut []))).await;78let store = store.data();79Ok(result.ok().map(|_| (yields, *store)))80}8182#[wasmtime_test(with = "#[tokio::test]")]83async fn epoch_yield_at_func_entry(config: &mut Config) -> Result<()> {84// Should yield at start of call to func $subfunc.85assert_eq!(86Some((1, 0)),87run_and_count_yields_or_trap(88config,89"90(module91(import \"\" \"bump_epoch\" (func $bump))92(func (export \"run\")93call $bump ;; bump epoch94call $subfunc) ;; call func; will notice new epoch and yield95(func $subfunc))96",971,98InterruptMode::Yield(1),99|_| {},100)101.await?102);103Ok(())104}105106#[wasmtime_test(with = "#[tokio::test]")]107async fn epoch_yield_at_loop_header(config: &mut Config) -> Result<()> {108// Should yield at top of loop, once per five iters.109assert_eq!(110Some((2, 0)),111run_and_count_yields_or_trap(112config,113"114(module115(import \"\" \"bump_epoch\" (func $bump))116(func (export \"run\")117(local $i i32)118(local.set $i (i32.const 10))119(loop $l120call $bump121(br_if $l (local.tee $i (i32.sub (local.get $i) (i32.const 1)))))))122",1230,124InterruptMode::Yield(5),125|_| {},126)127.await?128);129Ok(())130}131132#[wasmtime_test(with = "#[tokio::test]")]133async fn epoch_yield_immediate(config: &mut Config) -> Result<()> {134// We should see one yield immediately when the initial deadline135// is zero.136assert_eq!(137Some((1, 0)),138run_and_count_yields_or_trap(139config,140"141(module142(import \"\" \"bump_epoch\" (func $bump))143(func (export \"run\")))144",1450,146InterruptMode::Yield(1),147|_| {},148)149.await?150);151Ok(())152}153154#[wasmtime_test(with = "#[tokio::test]")]155async fn epoch_yield_only_once(config: &mut Config) -> Result<()> {156// We should yield from the subfunction, and then when we return157// to the outer function and hit another loop header, we should158// not yield again (the double-check block will reload the correct159// epoch).160assert_eq!(161Some((1, 0)),162run_and_count_yields_or_trap(163config,164"165(module166(import \"\" \"bump_epoch\" (func $bump))167(func (export \"run\")168(local $i i32)169(call $subfunc)170(local.set $i (i32.const 0))171(loop $l172(br_if $l (i32.eq (i32.const 10)173(local.tee $i (i32.add (i32.const 1) (local.get $i)))))))174(func $subfunc175(call $bump)))176",1771,178InterruptMode::Yield(1),179|_| {},180)181.await?182);183Ok(())184}185186#[wasmtime_test(with = "#[tokio::test]")]187async fn epoch_interrupt_infinite_loop(config: &mut Config) -> Result<()> {188assert_eq!(189None,190run_and_count_yields_or_trap(191config,192"193(module194(import \"\" \"bump_epoch\" (func $bump))195(func (export \"run\")196(loop $l197(br $l))))198",1991,200InterruptMode::Trap,201|engine| {202std::thread::spawn(move || {203std::thread::sleep(std::time::Duration::from_millis(50));204engine.increment_epoch();205});206},207)208.await?209);210Ok(())211}212213#[wasmtime_test(with = "#[tokio::test]")]214async fn epoch_interrupt_function_entries(config: &mut Config) -> Result<()> {215assert_eq!(216None,217run_and_count_yields_or_trap(218config,219"220(module221(import \"\" \"bump_epoch\" (func $bump))222(func (export \"run\")223call $f1224call $f1225call $f1226call $f1227call $f1228call $f1229call $f1230call $f1231call $f1232call $f1)233(func $f1234call $f2235call $f2236call $f2237call $f2238call $f2239call $f2240call $f2241call $f2242call $f2243call $f2)244(func $f2245call $f3246call $f3247call $f3248call $f3249call $f3250call $f3251call $f3252call $f3253call $f3254call $f3)255(func $f3256call $f4257call $f4258call $f4259call $f4260call $f4261call $f4262call $f4263call $f4264call $f4265call $f4)266(func $f4267call $f5268call $f5269call $f5270call $f5271call $f5272call $f5273call $f5274call $f5275call $f5276call $f5)277(func $f5278call $f6279call $f6280call $f6281call $f6282call $f6283call $f6284call $f6285call $f6286call $f6287call $f6)288(func $f6289call $f7290call $f7291call $f7292call $f7293call $f7294call $f7295call $f7296call $f7297call $f7298call $f7)299(func $f7300call $f8301call $f8302call $f8303call $f8304call $f8305call $f8306call $f8307call $f8308call $f8309call $f8)310(func $f8311call $f9312call $f9313call $f9314call $f9315call $f9316call $f9317call $f9318call $f9319call $f9320call $f9)321(func $f9))322",3231,324InterruptMode::Trap,325|engine| {326std::thread::spawn(move || {327std::thread::sleep(std::time::Duration::from_millis(50));328engine.increment_epoch();329});330},331)332.await?333);334Ok(())335}336337#[wasmtime_test(with = "#[tokio::test]")]338async fn epoch_callback_continue(config: &mut Config) -> Result<()> {339assert_eq!(340Some((0, 1)),341run_and_count_yields_or_trap(342config,343"344(module345(import \"\" \"bump_epoch\" (func $bump))346(func (export \"run\")347call $bump ;; bump epoch348call $subfunc) ;; call func; will notice new epoch and yield349(func $subfunc))350",3511,352InterruptMode::Callback(|mut cx| {353let s = cx.data_mut();354*s += 1;355Ok(UpdateDeadline::Continue(1))356}),357|_| {},358)359.await?360);361Ok(())362}363364#[wasmtime_test(with = "#[tokio::test]")]365async fn epoch_callback_yield(config: &mut Config) -> Result<()> {366assert_eq!(367Some((1, 1)),368run_and_count_yields_or_trap(369config,370"371(module372(import \"\" \"bump_epoch\" (func $bump))373(func (export \"run\")374call $bump ;; bump epoch375call $subfunc) ;; call func; will notice new epoch and yield376(func $subfunc))377",3781,379InterruptMode::Callback(|mut cx| {380let s = cx.data_mut();381*s += 1;382Ok(UpdateDeadline::Yield(1))383}),384|_| {},385)386.await?387);388389Ok(())390}391392#[wasmtime_test(with = "#[tokio::test]")]393async fn epoch_callback_yield_custom(config: &mut Config) -> Result<()> {394assert_eq!(395Some((1, 1)),396run_and_count_yields_or_trap(397config,398"399(module400(import \"\" \"bump_epoch\" (func $bump))401(func (export \"run\")402call $bump ;; bump epoch403call $subfunc) ;; call func; will notice new epoch and yield404(func $subfunc))405",4061,407InterruptMode::Callback(|mut cx| {408let s = cx.data_mut();409*s += 1;410let fut = Box::pin(tokio::task::yield_now());411Ok(UpdateDeadline::YieldCustom(1, fut))412}),413|_| {},414)415.await?416);417Ok(())418}419420#[wasmtime_test(with = "#[tokio::test]")]421async fn epoch_callback_trap(config: &mut Config) -> Result<()> {422assert_eq!(423None,424run_and_count_yields_or_trap(425config,426"427(module428(import \"\" \"bump_epoch\" (func $bump))429(func (export \"run\")430call $bump ;; bump epoch431call $subfunc) ;; call func; will notice new epoch and yield432(func $subfunc))433",4341,435InterruptMode::Callback(|_| Err(anyhow!("Failing in callback"))),436|_| {},437)438.await?439);440Ok(())441}442443#[wasmtime_test(with = "#[tokio::test]")]444async fn drop_future_on_epoch_yield(config: &mut Config) -> Result<()> {445let wasm = "446(module447(import \"\" \"bump_epoch\" (func $bump))448(import \"\" \"im_alive\" (func $im_alive))449(import \"\" \"oops\" (func $oops))450(func (export \"run\")451(call $im_alive)452(call $bump)453(call $subfunc) ;; subfunc entry to do epoch check454(call $oops))455(func $subfunc))456";457458let engine = build_engine(config)?;459let mut linker = make_env::<()>(&engine);460461// Create a few helpers for the Wasm to call.462let alive_flag = Arc::new(AtomicBool::new(false));463let alive_flag_clone = alive_flag.clone();464linker465.func_new(466"",467"oops",468FuncType::new(&engine, None, None),469move |_caller, _params, _results| {470panic!("Should not have reached this point!");471},472)473.unwrap();474linker475.func_new(476"",477"im_alive",478FuncType::new(&engine, None, None),479move |_caller, _params, _results| {480alive_flag_clone.store(true, Ordering::Release);481Ok(())482},483)484.unwrap();485486let module = Module::new(&engine, wasm).unwrap();487let mut store = Store::new(&engine, ());488489store.set_epoch_deadline(1);490store.epoch_deadline_async_yield_and_update(1);491492let instance = linker.instantiate_async(&mut store, &module).await.unwrap();493let f = instance.get_func(&mut store, "run").unwrap();494let _ = PollOnce::new(Box::pin(f.call_async(&mut store, &[], &mut []))).await;495496assert_eq!(true, alive_flag.load(Ordering::Acquire));497Ok(())498}499500501