Path: blob/main/tests/all/component_model/async.rs
3081 views
use crate::async_functions::{PollOnce, execute_across_threads};1use std::pin::Pin;2use std::task::{Context, Poll};3use wasmtime::Result;4use wasmtime::{AsContextMut, Config, Engine, Store, StoreContextMut, Trap, component::*};5use wasmtime_component_util::REALLOC_AND_FREE;67/// This is super::func::thunks, except with an async store.8#[tokio::test]9#[cfg_attr(miri, ignore)]10async fn smoke() -> Result<()> {11let component = r#"12(component13(core module $m14(func (export "thunk"))15(func (export "thunk-trap") unreachable)16)17(core instance $i (instantiate $m))18(func (export "thunk")19(canon lift (core func $i "thunk"))20)21(func (export "thunk-trap")22(canon lift (core func $i "thunk-trap"))23)24)25"#;2627let engine = super::async_engine();28let component = Component::new(&engine, component)?;29let mut store = Store::new(&engine, ());30let instance = Linker::new(&engine)31.instantiate_async(&mut store, &component)32.await?;3334let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;3536thunk.call_async(&mut store, ()).await?;3738let err = instance39.get_typed_func::<(), ()>(&mut store, "thunk-trap")?40.call_async(&mut store, ())41.await42.unwrap_err();43assert_eq!(err.downcast::<Trap>()?, Trap::UnreachableCodeReached);4445Ok(())46}4748/// Handle an import function, created using component::Linker::func_wrap_async.49#[tokio::test]50#[cfg_attr(miri, ignore)]51async fn smoke_func_wrap() -> Result<()> {52let component = r#"53(component54(type $f (func))55(import "i" (func $f))5657(core module $m58(import "imports" "i" (func $i))59(func (export "thunk") call $i)60)6162(core func $f (canon lower (func $f)))63(core instance $i (instantiate $m64(with "imports" (instance65(export "i" (func $f))66))67))68(func (export "thunk")69(canon lift (core func $i "thunk"))70)71)72"#;7374let engine = super::async_engine();75let component = Component::new(&engine, component)?;76let mut store = Store::new(&engine, ());77let mut linker = Linker::new(&engine);78let mut root = linker.root();79root.func_wrap_async("i", |_: StoreContextMut<()>, _: ()| {80Box::new(async { Ok(()) })81})?;8283let instance = linker.instantiate_async(&mut store, &component).await?;8485let thunk = instance.get_typed_func::<(), ()>(&mut store, "thunk")?;8687thunk.call_async(&mut store, ()).await?;8889Ok(())90}9192// This test stresses TLS management in combination with the `realloc` option93// for imported functions. This will create an async computation which invokes a94// component that invokes an imported function. The imported function returns a95// list which will require invoking malloc.96//97// As an added stressor all polls are sprinkled across threads through98// `execute_across_threads`. Yields are injected liberally by configuring 199// fuel consumption to trigger a yield.100//101// Overall a yield should happen during malloc which should be an "interesting102// situation" with respect to the runtime.103#[tokio::test]104#[cfg_attr(miri, ignore)]105async fn resume_separate_thread() -> Result<()> {106let mut config = wasmtime_test_util::component::config();107config.consume_fuel(true);108let engine = Engine::new(&config)?;109let component = format!(110r#"111(component112(import "yield" (func $yield (result (list u8))))113(core module $libc114(memory (export "memory") 1)115{REALLOC_AND_FREE}116)117(core instance $libc (instantiate $libc))118119(core func $yield120(canon lower121(func $yield)122(memory $libc "memory")123(realloc (func $libc "realloc"))124)125)126127(core module $m128(import "" "yield" (func $yield (param i32)))129(import "libc" "memory" (memory 0))130(func $start131i32.const 8132call $yield133)134(start $start)135)136(core instance (instantiate $m137(with "" (instance (export "yield" (func $yield))))138(with "libc" (instance $libc))139))140)141"#142);143let component = Component::new(&engine, component)?;144let mut linker = Linker::new(&engine);145linker146.root()147.func_wrap_async("yield", |_: StoreContextMut<()>, _: ()| {148Box::new(async {149tokio::task::yield_now().await;150Ok((vec![1u8, 2u8],))151})152})?;153154execute_across_threads(async move {155let mut store = Store::new(&engine, ());156store.set_fuel(u64::MAX).unwrap();157store.fuel_async_yield_interval(Some(1)).unwrap();158linker.instantiate_async(&mut store, &component).await?;159Ok::<_, wasmtime::Error>(())160})161.await?;162Ok(())163}164165// This test is intended to stress TLS management in the component model around166// the management of the `realloc` function. This creates an async computation167// representing the execution of a component model function where entry into the168// component uses `realloc` and then the component runs. This async computation169// is then polled iteratively with another "wasm activation" (in this case a170// core wasm function) on the stack. The poll-per-call should work and nothing171// should in theory have problems here.172//173// As an added stressor all polls are sprinkled across threads through174// `execute_across_threads`. Yields are injected liberally by configuring 1175// fuel consumption to trigger a yield.176//177// Overall a yield should happen during malloc which should be an "interesting178// situation" with respect to the runtime.179#[tokio::test]180#[cfg_attr(miri, ignore)]181async fn poll_through_wasm_activation() -> Result<()> {182let mut config = wasmtime_test_util::component::config();183config.consume_fuel(true);184let engine = Engine::new(&config)?;185let component = format!(186r#"187(component188(core module $m189{REALLOC_AND_FREE}190(memory (export "memory") 1)191(func (export "run") (param i32 i32)192)193)194(core instance $i (instantiate $m))195(func (export "run") (param "x" (list u8))196(canon lift (core func $i "run")197(memory $i "memory")198(realloc (func $i "realloc"))))199)200"#201);202let component = Component::new(&engine, component)?;203let linker = Linker::new(&engine);204205let invoke_component = {206let engine = engine.clone();207async move {208let mut store = Store::new(&engine, ());209store.set_fuel(u64::MAX).unwrap();210store.fuel_async_yield_interval(Some(1)).unwrap();211let instance = linker.instantiate_async(&mut store, &component).await?;212let func = instance.get_typed_func::<(Vec<u8>,), ()>(&mut store, "run")?;213func.call_async(&mut store, (vec![1, 2, 3],)).await?;214Ok::<_, wasmtime::Error>(())215}216};217218execute_across_threads(async move {219let mut store = Store::new(&engine, Some(Box::pin(invoke_component)));220let poll_once = wasmtime::Func::wrap_async(&mut store, |mut cx, _: ()| {221let invoke_component = cx.data_mut().take().unwrap();222Box::new(async move {223match PollOnce::new(invoke_component).await {224Ok(result) => {225result?;226Ok(1)227}228Err(future) => {229*cx.data_mut() = Some(future);230Ok(0)231}232}233})234});235let poll_once = poll_once.typed::<(), i32>(&mut store)?;236while poll_once.call_async(&mut store, ()).await? != 1 {237// loop around to call again238}239Ok::<_, wasmtime::Error>(())240})241.await?;242Ok(())243}244245/// Test async drop method for host resources.246#[tokio::test]247#[cfg_attr(miri, ignore)]248async fn drop_resource_async() -> Result<()> {249use std::sync::Arc;250use std::sync::Mutex;251252let engine = super::async_engine();253let c = Component::new(254&engine,255r#"256(component257(import "t" (type $t (sub resource)))258259(core func $drop (canon resource.drop $t))260261(core module $m262(import "" "drop" (func $drop (param i32)))263(func (export "f") (param i32)264(call $drop (local.get 0))265)266)267(core instance $i (instantiate $m268(with "" (instance269(export "drop" (func $drop))270))271))272273(func (export "f") (param "x" (own $t))274(canon lift (core func $i "f")))275)276"#,277)?;278279struct MyType;280281let mut store = Store::new(&engine, ());282let mut linker = Linker::new(&engine);283284let drop_status = Arc::new(Mutex::new("not dropped"));285let ds = drop_status.clone();286287linker288.root()289.resource_async("t", ResourceType::host::<MyType>(), move |_, _| {290let ds = ds.clone();291Box::new(async move {292*ds.lock().unwrap() = "before yield";293tokio::task::yield_now().await;294*ds.lock().unwrap() = "after yield";295Ok(())296})297})?;298let i = linker.instantiate_async(&mut store, &c).await?;299let f = i.get_typed_func::<(Resource<MyType>,), ()>(&mut store, "f")?;300301execute_across_threads(async move {302let resource = Resource::new_own(100);303f.call_async(&mut store, (resource,)).await?;304Ok::<_, wasmtime::Error>(())305})306.await?;307308assert_eq!("after yield", *drop_status.lock().unwrap());309310Ok(())311}312313/// Test task deletion in three situations, for every combination of lift/lower/(guest/host):314/// 1. An explicit thread calls task.return315/// 2. An explicit thread suspends indefinitely316/// 3. An explicit thread yield loops indefinitely317#[tokio::test]318#[cfg_attr(miri, ignore)]319async fn task_deletion() -> Result<()> {320let mut config = Config::new();321config.wasm_component_model_async(true);322config.wasm_component_model_threading(true);323config.wasm_component_model_async_stackful(true);324config.wasm_component_model_async_builtins(true);325let engine = Engine::new(&config)?;326let component = Component::new(327&engine,328r#"(component329(component $C330(core module $Memory (memory (export "mem") 1))331(core instance $memory (instantiate $Memory))332;; Defines the table for the thread start functions333(core module $libc334(table (export "__indirect_function_table") 3 funcref))335(core module $CM336(import "" "mem" (memory 1))337(import "" "task.return" (func $task-return (param i32)))338(import "" "task.cancel" (func $task-cancel))339(import "" "thread.new-indirect" (func $thread-new-indirect (param i32 i32) (result i32)))340(import "" "thread.suspend" (func $thread-suspend (result i32)))341(import "" "thread.suspend-cancellable" (func $thread-suspend-cancellable (result i32)))342(import "" "thread.yield-to" (func $thread-yield-to (param i32) (result i32)))343(import "" "thread.yield-to-cancellable" (func $thread-yield-to-cancellable (param i32) (result i32)))344(import "" "thread.switch-to" (func $thread-switch-to (param i32) (result i32)))345(import "" "thread.switch-to-cancellable" (func $thread-switch-to-cancellable (param i32) (result i32)))346(import "" "thread.yield" (func $thread-yield (result i32)))347(import "" "thread.yield-cancellable" (func $thread-yield-cancellable (result i32)))348(import "" "thread.index" (func $thread-index (result i32)))349(import "" "thread.resume-later" (func $thread-resume-later (param i32)))350(import "" "waitable.join" (func $waitable.join (param i32 i32)))351(import "" "waitable-set.new" (func $waitable-set.new (result i32)))352(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))353(import "libc" "__indirect_function_table" (table $indirect-function-table 3 funcref))354355;; Indices into the function table for the thread start functions356(global $call-return-ftbl-idx i32 (i32.const 0))357(global $suspend-ftbl-idx i32 (i32.const 1))358(global $yield-loop-ftbl-idx i32 (i32.const 2))359360(func $call-return (param i32)361(call $task-return (local.get 0)))362363(func $suspend (param i32)364(drop (call $thread-suspend)))365366(func $yield-loop (param i32)367(loop $top368(drop (call $thread-yield))369(br $top)))370371(func (export "explicit-thread-calls-return-stackful")372(call $thread-resume-later373(call $thread-new-indirect (global.get $call-return-ftbl-idx) (i32.const 42))))374375(func (export "explicit-thread-calls-return-stackless") (result i32)376(call $thread-resume-later377(call $thread-new-indirect (global.get $call-return-ftbl-idx) (i32.const 42)))378(i32.const 0 (; EXIT ;)))379380(func (export "cb") (param i32 i32 i32) (result i32)381(unreachable))382383(func (export "explicit-thread-suspends-sync") (result i32)384(call $thread-resume-later385(call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))386(i32.const 42))387388(func (export "explicit-thread-suspends-stackful")389(call $thread-resume-later390(call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))391(call $task-return (i32.const 42)))392393(func (export "explicit-thread-suspends-stackless") (result i32)394(call $thread-resume-later395(call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))396(call $task-return (i32.const 42))397(i32.const 0))398399(func (export "explicit-thread-yield-loops-sync") (result i32)400(call $thread-resume-later401(call $thread-new-indirect (global.get $yield-loop-ftbl-idx) (i32.const 42)))402(i32.const 42))403404(func (export "explicit-thread-yield-loops-stackful")405(call $thread-resume-later406(call $thread-new-indirect (global.get $yield-loop-ftbl-idx) (i32.const 42)))407(call $task-return (i32.const 42)))408409(func (export "explicit-thread-yield-loops-stackless") (result i32)410(call $thread-resume-later411(call $thread-new-indirect (global.get $suspend-ftbl-idx) (i32.const 42)))412(call $task-return (i32.const 42))413(i32.const 0 (; EXIT ;)))414415;; Initialize the function table that will be used by thread.new-indirect416(elem (table $indirect-function-table) (i32.const 0 (; call-return-ftbl-idx ;)) func $call-return)417(elem (table $indirect-function-table) (i32.const 1 (; suspend-ftbl-idx ;)) func $suspend)418(elem (table $indirect-function-table) (i32.const 2 (; yield-loop-ftbl-idx ;)) func $yield-loop)419)420421;; Instantiate the libc module to get the table422(core instance $libc (instantiate $libc))423;; Get access to `thread.new-indirect` that uses the table from libc424(core type $start-func-ty (func (param i32)))425(alias core export $libc "__indirect_function_table" (core table $indirect-function-table))426427(core func $task-return (canon task.return (result u32)))428(core func $task-cancel (canon task.cancel))429(core func $thread-new-indirect430(canon thread.new-indirect $start-func-ty (table $indirect-function-table)))431(core func $thread-yield (canon thread.yield))432(core func $thread-yield-cancellable (canon thread.yield cancellable))433(core func $thread-index (canon thread.index))434(core func $thread-yield-to (canon thread.yield-to))435(core func $thread-yield-to-cancellable (canon thread.yield-to cancellable))436(core func $thread-resume-later (canon thread.resume-later))437(core func $thread-switch-to (canon thread.switch-to))438(core func $thread-switch-to-cancellable (canon thread.switch-to cancellable))439(core func $thread-suspend (canon thread.suspend))440(core func $thread-suspend-cancellable (canon thread.suspend cancellable))441(core func $waitable-set.new (canon waitable-set.new))442(core func $waitable.join (canon waitable.join))443(core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))444445;; Instantiate the main module446(core instance $cm (447instantiate $CM448(with "" (instance449(export "mem" (memory $memory "mem"))450(export "task.return" (func $task-return))451(export "task.cancel" (func $task-cancel))452(export "thread.new-indirect" (func $thread-new-indirect))453(export "thread.index" (func $thread-index))454(export "thread.yield-to" (func $thread-yield-to))455(export "thread.yield-to-cancellable" (func $thread-yield-to-cancellable))456(export "thread.yield" (func $thread-yield))457(export "thread.yield-cancellable" (func $thread-yield-cancellable))458(export "thread.switch-to" (func $thread-switch-to))459(export "thread.switch-to-cancellable" (func $thread-switch-to-cancellable))460(export "thread.suspend" (func $thread-suspend))461(export "thread.suspend-cancellable" (func $thread-suspend-cancellable))462(export "thread.resume-later" (func $thread-resume-later))463(export "waitable.join" (func $waitable.join))464(export "waitable-set.wait" (func $waitable-set.wait))465(export "waitable-set.new" (func $waitable-set.new))))466(with "libc" (instance $libc))))467468(func (export "explicit-thread-calls-return-stackful") async (result u32)469(canon lift (core func $cm "explicit-thread-calls-return-stackful") async))470(func (export "explicit-thread-calls-return-stackless") async (result u32)471(canon lift (core func $cm "explicit-thread-calls-return-stackless") async (callback (func $cm "cb"))))472(func (export "explicit-thread-suspends-sync") async (result u32)473(canon lift (core func $cm "explicit-thread-suspends-sync")))474(func (export "explicit-thread-suspends-stackful") async (result u32)475(canon lift (core func $cm "explicit-thread-suspends-stackful") async))476(func (export "explicit-thread-suspends-stackless") async (result u32)477(canon lift (core func $cm "explicit-thread-suspends-stackless") async (callback (func $cm "cb"))))478(func (export "explicit-thread-yield-loops-sync") async (result u32)479(canon lift (core func $cm "explicit-thread-yield-loops-sync")))480(func (export "explicit-thread-yield-loops-stackful") async (result u32)481(canon lift (core func $cm "explicit-thread-yield-loops-stackful") async))482(func (export "explicit-thread-yield-loops-stackless") async (result u32)483(canon lift (core func $cm "explicit-thread-yield-loops-stackless") async (callback (func $cm "cb"))))484)485486(component $D487(import "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful async (result u32)))488(import "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless async (result u32)))489(import "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync async (result u32)))490(import "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful async (result u32)))491(import "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless async (result u32)))492(import "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync async (result u32)))493(import "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful async (result u32)))494(import "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless async (result u32)))495496(core module $Memory (memory (export "mem") 1))497(core instance $memory (instantiate $Memory))498(core module $DM499(import "" "mem" (memory 1))500(import "" "subtask.cancel" (func $subtask.cancel (param i32) (result i32)))501;; sync lowered502(import "" "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful (result i32)))503(import "" "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless (result i32)))504(import "" "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync (result i32)))505(import "" "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful (result i32)))506(import "" "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless (result i32)))507(import "" "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync (result i32)))508(import "" "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful (result i32)))509(import "" "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless (result i32)))510;; async lowered511(import "" "explicit-thread-calls-return-stackful-async" (func $explicit-thread-calls-return-stackful-async (param i32) (result i32)))512(import "" "explicit-thread-calls-return-stackless-async" (func $explicit-thread-calls-return-stackless-async (param i32) (result i32)))513(import "" "explicit-thread-suspends-sync-async" (func $explicit-thread-suspends-sync-async (param i32) (result i32)))514(import "" "explicit-thread-suspends-stackful-async" (func $explicit-thread-suspends-stackful-async (param i32) (result i32)))515(import "" "explicit-thread-suspends-stackless-async" (func $explicit-thread-suspends-stackless-async (param i32) (result i32)))516(import "" "explicit-thread-yield-loops-sync-async" (func $explicit-thread-yield-loops-sync-async (param i32) (result i32)))517(import "" "explicit-thread-yield-loops-stackful-async" (func $explicit-thread-yield-loops-stackful-async (param i32) (result i32)))518(import "" "explicit-thread-yield-loops-stackless-async" (func $explicit-thread-yield-loops-stackless-async (param i32) (result i32)))519(import "" "waitable.join" (func $waitable.join (param i32 i32)))520(import "" "waitable-set.new" (func $waitable-set.new (result i32)))521(import "" "waitable-set.wait" (func $waitable-set.wait (param i32 i32) (result i32)))522(import "" "thread.yield" (func $thread-yield (result i32)))523524(func $check (param i32)525(if (i32.ne (local.get 0) (i32.const 42))526(then unreachable))527)528529(func $check-async (param i32)530(local $retp i32) (local $ws i32) (local $ws-retp i32)531(local.set $retp (i32.const 8))532(local.set $ws-retp (i32.const 16))533(local.set $ws (call $waitable-set.new))534535(if (i32.eq (i32.and (local.get 0) (i32.const 0xF)) (i32.const 2 (; RETURNED ;)))536(then (call $check (i32.load (local.get $retp))))537(else538(call $waitable.join (i32.shr_u (local.get 0) (i32.const 4)) (local.get $ws))539(drop (call $waitable-set.wait (local.get $ws) (local.get $ws-retp)))540(call $check (i32.load (local.get $retp)))))541)542543(func $run (export "run") (result i32)544(local $retp i32)545(local.set $retp (i32.const 8))546(call $check (call $explicit-thread-calls-return-stackless))547(call $check (call $explicit-thread-calls-return-stackful))548(call $check (call $explicit-thread-suspends-sync))549(call $check (call $explicit-thread-suspends-stackful))550(call $check (call $explicit-thread-suspends-stackless))551(call $check (call $explicit-thread-yield-loops-sync))552(call $check (call $explicit-thread-yield-loops-stackful))553(call $check (call $explicit-thread-yield-loops-stackless))554555(call $check-async (call $explicit-thread-calls-return-stackless-async (local.get $retp)))556(call $check-async (call $explicit-thread-calls-return-stackful-async (local.get $retp)))557(call $check-async (call $explicit-thread-suspends-sync-async (local.get $retp)))558(call $check-async (call $explicit-thread-suspends-stackful-async (local.get $retp)))559(call $check-async (call $explicit-thread-suspends-stackless-async (local.get $retp)))560(call $check-async (call $explicit-thread-yield-loops-sync-async (local.get $retp)))561(call $check-async (call $explicit-thread-yield-loops-stackful-async (local.get $retp)))562(call $check-async (call $explicit-thread-yield-loops-stackless-async (local.get $retp)))563564(i32.const 42)565)566)567568(core func $waitable-set.new (canon waitable-set.new))569(core func $waitable-set.wait (canon waitable-set.wait (memory $memory "mem")))570(core func $waitable.join (canon waitable.join))571(core func $subtask.cancel (canon subtask.cancel async))572(core func $thread.yield (canon thread.yield))573;; sync lowered574(canon lower (func $explicit-thread-calls-return-stackful) (memory $memory "mem") (core func $explicit-thread-calls-return-stackful'))575(canon lower (func $explicit-thread-calls-return-stackless) (memory $memory "mem") (core func $explicit-thread-calls-return-stackless'))576(canon lower (func $explicit-thread-suspends-sync) (memory $memory "mem") (core func $explicit-thread-suspends-sync'))577(canon lower (func $explicit-thread-suspends-stackful) (memory $memory "mem") (core func $explicit-thread-suspends-stackful'))578(canon lower (func $explicit-thread-suspends-stackless) (memory $memory "mem") (core func $explicit-thread-suspends-stackless'))579(canon lower (func $explicit-thread-yield-loops-sync) (memory $memory "mem") (core func $explicit-thread-yield-loops-sync'))580(canon lower (func $explicit-thread-yield-loops-stackful) (memory $memory "mem") (core func $explicit-thread-yield-loops-stackful'))581(canon lower (func $explicit-thread-yield-loops-stackless) (memory $memory "mem") (core func $explicit-thread-yield-loops-stackless'))582;; async lowered583(canon lower (func $explicit-thread-calls-return-stackful) async (memory $memory "mem") (core func $explicit-thread-calls-return-stackful-async'))584(canon lower (func $explicit-thread-calls-return-stackless) async (memory $memory "mem") (core func $explicit-thread-calls-return-stackless-async'))585(canon lower (func $explicit-thread-suspends-sync) async (memory $memory "mem") (core func $explicit-thread-suspends-sync-async'))586(canon lower (func $explicit-thread-suspends-stackful) async (memory $memory "mem") (core func $explicit-thread-suspends-stackful-async'))587(canon lower (func $explicit-thread-suspends-stackless) async (memory $memory "mem") (core func $explicit-thread-suspends-stackless-async'))588(canon lower (func $explicit-thread-yield-loops-sync) async (memory $memory "mem") (core func $explicit-thread-yield-loops-sync-async'))589(canon lower (func $explicit-thread-yield-loops-stackful) async (memory $memory "mem") (core func $explicit-thread-yield-loops-stackful-async'))590(canon lower (func $explicit-thread-yield-loops-stackless) async (memory $memory "mem") (core func $explicit-thread-yield-loops-stackless-async'))591(core instance $dm (instantiate $DM (with "" (instance592(export "mem" (memory $memory "mem"))593(export "explicit-thread-calls-return-stackful" (func $explicit-thread-calls-return-stackful'))594(export "explicit-thread-calls-return-stackless" (func $explicit-thread-calls-return-stackless'))595(export "explicit-thread-suspends-sync" (func $explicit-thread-suspends-sync'))596(export "explicit-thread-suspends-stackful" (func $explicit-thread-suspends-stackful'))597(export "explicit-thread-suspends-stackless" (func $explicit-thread-suspends-stackless'))598(export "explicit-thread-yield-loops-sync" (func $explicit-thread-yield-loops-sync'))599(export "explicit-thread-yield-loops-stackful" (func $explicit-thread-yield-loops-stackful'))600(export "explicit-thread-yield-loops-stackless" (func $explicit-thread-yield-loops-stackless'))601(export "explicit-thread-calls-return-stackful-async" (func $explicit-thread-calls-return-stackful-async'))602(export "explicit-thread-calls-return-stackless-async" (func $explicit-thread-calls-return-stackless-async'))603(export "explicit-thread-suspends-sync-async" (func $explicit-thread-suspends-sync-async'))604(export "explicit-thread-suspends-stackful-async" (func $explicit-thread-suspends-stackful-async'))605(export "explicit-thread-suspends-stackless-async" (func $explicit-thread-suspends-stackless-async'))606(export "explicit-thread-yield-loops-sync-async" (func $explicit-thread-yield-loops-sync-async'))607(export "explicit-thread-yield-loops-stackful-async" (func $explicit-thread-yield-loops-stackful-async'))608(export "explicit-thread-yield-loops-stackless-async" (func $explicit-thread-yield-loops-stackless-async'))609(export "waitable.join" (func $waitable.join))610(export "waitable-set.new" (func $waitable-set.new))611(export "waitable-set.wait" (func $waitable-set.wait))612(export "subtask.cancel" (func $subtask.cancel))613(export "thread.yield" (func $thread.yield))614))))615(func (export "run") async (result u32) (canon lift (core func $dm "run")))616)617618(instance $c (instantiate $C))619(instance $d (instantiate $D620(with "explicit-thread-calls-return-stackful" (func $c "explicit-thread-calls-return-stackful"))621(with "explicit-thread-calls-return-stackless" (func $c "explicit-thread-calls-return-stackless"))622(with "explicit-thread-suspends-sync" (func $c "explicit-thread-suspends-sync"))623(with "explicit-thread-suspends-stackful" (func $c "explicit-thread-suspends-stackful"))624(with "explicit-thread-suspends-stackless" (func $c "explicit-thread-suspends-stackless"))625(with "explicit-thread-yield-loops-sync" (func $c "explicit-thread-yield-loops-sync"))626(with "explicit-thread-yield-loops-stackful" (func $c "explicit-thread-yield-loops-stackful"))627(with "explicit-thread-yield-loops-stackless" (func $c "explicit-thread-yield-loops-stackless"))628))629(func (export "run") (alias export $d "run"))630(func (export "explicit-thread-calls-return-stackful") (alias export $c "explicit-thread-calls-return-stackful"))631(func (export "explicit-thread-calls-return-stackless") (alias export $c "explicit-thread-calls-return-stackless"))632(func (export "explicit-thread-suspends-sync") (alias export $c "explicit-thread-suspends-sync"))633(func (export "explicit-thread-suspends-stackful") (alias export $c "explicit-thread-suspends-stackful"))634(func (export "explicit-thread-suspends-stackless") (alias export $c "explicit-thread-suspends-stackless"))635(func (export "explicit-thread-yield-loops-sync") (alias export $c "explicit-thread-yield-loops-sync"))636(func (export "explicit-thread-yield-loops-stackful") (alias export $c "explicit-thread-yield-loops-stackful"))637(func (export "explicit-thread-yield-loops-stackless") (alias export $c "explicit-thread-yield-loops-stackless"))638)639"#,640)?641.serialize()?;642643let component = unsafe { Component::deserialize(&engine, &component)? };644let mut store = Store::new(&engine, ());645let instance = Linker::new(&engine)646.instantiate_async(&mut store, &component)647.await?;648let funcs = vec![649"run",650"explicit-thread-calls-return-stackful",651"explicit-thread-calls-return-stackless",652"explicit-thread-suspends-sync",653"explicit-thread-suspends-stackful",654"explicit-thread-suspends-stackless",655"explicit-thread-yield-loops-sync",656"explicit-thread-yield-loops-stackful",657"explicit-thread-yield-loops-stackless",658];659for func in funcs {660let func = instance.get_typed_func::<(), (u32,)>(&mut store, func)?;661assert_eq!(func.call_async(&mut store, ()).await?, (42,));662}663664Ok(())665}666667#[tokio::test]668#[cfg_attr(miri, ignore)]669async fn cancel_host_future() -> Result<()> {670let mut config = Config::new();671config.wasm_component_model_async(true);672let engine = Engine::new(&config)?;673674let component = Component::new(675&engine,676r#"677(component678(core module $libc (memory (export "memory") 1))679(core instance $libc (instantiate $libc))680(core module $m681(import "" "future.read" (func $future.read (param i32 i32) (result i32)))682(import "" "future.cancel-read" (func $future.cancel-read (param i32) (result i32)))683(memory (export "memory") 1)684685(func (export "run") (param i32)686;; read/cancel attempt 1687(call $future.read (local.get 0) (i32.const 100))688i32.const -1 ;; BLOCKED689i32.ne690if unreachable end691692(call $future.cancel-read (local.get 0))693i32.const 2 ;; CANCELLED694i32.ne695if unreachable end696697;; read/cancel attempt 2698(call $future.read (local.get 0) (i32.const 100))699i32.const -1 ;; BLOCKED700i32.ne701if unreachable end702703(call $future.cancel-read (local.get 0))704i32.const 2 ;; CANCELLED705i32.ne706if unreachable end707)708)709710(type $f (future u32))711(core func $future.read (canon future.read $f async (memory $libc "memory")))712(core func $future.cancel-read (canon future.cancel-read $f))713714(core instance $i (instantiate $m715(with "" (instance716(export "future.read" (func $future.read))717(export "future.cancel-read" (func $future.cancel-read))718))719))720721(func (export "run") async (param "f" $f)722(canon lift723(core func $i "run")724(memory $libc "memory")725)726)727)728"#,729)?;730731let mut store = Store::new(&engine, ());732let instance = Linker::new(&engine)733.instantiate_async(&mut store, &component)734.await?;735let func = instance.get_typed_func::<(FutureReader<u32>,), ()>(&mut store, "run")?;736let reader = FutureReader::new(&mut store, MyFutureReader);737func.call_async(&mut store, (reader,)).await?;738739return Ok(());740741struct MyFutureReader;742743impl FutureProducer<()> for MyFutureReader {744type Item = u32;745746fn poll_produce(747self: Pin<&mut Self>,748_cx: &mut Context<'_>,749_store: StoreContextMut<()>,750finish: bool,751) -> Poll<Result<Option<Self::Item>>> {752if finish {753Poll::Ready(Ok(None))754} else {755Poll::Pending756}757}758}759}760761#[tokio::test]762#[cfg_attr(miri, ignore)]763async fn run_wasm_in_call_async() -> Result<()> {764_ = env_logger::try_init();765766let mut config = Config::new();767config.wasm_component_model_async(true);768let engine = Engine::new(&config)?;769770let a = Component::new(771&engine,772r#"773(component774(type $t (func async))775(import "a" (func $f (type $t)))776(core func $f (canon lower (func $f)))777(core module $a778(import "" "f" (func $f))779(func (export "run") call $f)780)781(core instance $a (instantiate $a782(with "" (instance (export "f" (func $f))))783))784(func (export "run") (type $t)785(canon lift (core func $a "run")))786)787"#,788)?;789let b = Component::new(790&engine,791r#"792(component793(type $t (func async))794(core module $a795(func (export "run"))796)797(core instance $a (instantiate $a))798(func (export "run") (type $t)799(canon lift (core func $a "run")))800)801"#,802)?;803804type State = Option<Instance>;805806let mut linker = Linker::new(&engine);807linker808.root()809.func_wrap_concurrent("a", |accessor: &Accessor<State>, (): ()| {810Box::pin(async move {811let func = accessor.with(|mut access| {812access813.get()814.unwrap()815.get_typed_func::<(), ()>(&mut access, "run")816})?;817func.call_concurrent(accessor, ()).await?;818Ok(())819})820})?;821let mut store = Store::new(&engine, None);822let instance_a = linker.instantiate_async(&mut store, &a).await?;823let instance_b = linker.instantiate_async(&mut store, &b).await?;824*store.data_mut() = Some(instance_b);825let run = instance_a.get_typed_func::<(), ()>(&mut store, "run")?;826run.call_async(&mut store, ()).await?;827Ok(())828}829830#[tokio::test]831#[cfg_attr(miri, ignore)]832async fn require_concurrency_support() -> Result<()> {833let mut config = Config::new();834config.concurrency_support(false);835let engine = Engine::new(&config)?;836837let mut store = Store::new(&engine, ());838839assert!(840store841.run_concurrent(async |_| wasmtime::error::Ok(()))842.await843.is_err()844);845846let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {847StreamReader::<u32>::new(&mut store, Vec::new());848}));849assert!(ok.is_err());850851let ok = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {852FutureReader::new(&mut store, async { wasmtime::error::Ok(0) })853}));854assert!(ok.is_err());855856let mut linker = Linker::<()>::new(&engine);857let mut root = linker.root();858859assert!(860root.func_wrap_concurrent::<(), (), _>("f1", |_, _| { todo!() })861.is_err()862);863assert!(864root.func_new_concurrent("f2", |_, _, _, _| { todo!() })865.is_err()866);867assert!(868root.resource_concurrent("f3", ResourceType::host::<u32>(), |_, _| { todo!() })869.is_err()870);871872Ok(())873}874875#[tokio::test]876#[cfg_attr(miri, ignore)]877async fn cancel_host_task_does_not_leak() -> Result<()> {878let mut config = Config::new();879config.wasm_component_model_async(true);880let engine = Engine::new(&config)?;881882let mut store = Store::new(&engine, ());883let component = Component::new(884&engine,885r#"(component886(import "f" (func $f async))887888(core module $m889(import "" "f" (func $f (result i32)))890(import "" "cancel" (func $cancel (param i32) (result i32)))891(import "" "drop" (func $drop (param i32)))892(func (export "run")893(local i32)894895;; start the subtask, asserting it's `STARTED`896call $f897local.tee 0898i32.const 0xf899i32.and900i32.const 1 ;; STARTED901i32.ne902if unreachable end903904;; extract the task id905local.get 0906i32.const 4907i32.shr_u908local.set 0909910;; cancel the subtask asserting it's `RETURN_CANCELLED`911local.get 0912call $cancel913i32.const 4 ;; RETURN_CANCELLED914i32.ne915if unreachable end916917;; drop the subtask918local.get 0919call $drop920)921)922(core func $f (canon lower (func $f) async))923(core func $cancel (canon subtask.cancel))924(core func $drop (canon subtask.drop))925(core instance $i (instantiate $m926(with "" (instance927(export "f" (func $f))928(export "cancel" (func $cancel))929(export "drop" (func $drop))930))931))932933(func (export "f") async934(canon lift (core func $i "run")))935936937)"#,938)?;939940let mut linker = Linker::new(&engine);941linker.root().func_wrap_concurrent("f", |_, ()| {942Box::pin(async move {943std::future::pending::<()>().await;944Ok(())945})946})?;947let instance = linker.instantiate_async(&mut store, &component).await?;948let func = instance.get_typed_func::<(), ()>(&mut store, "f")?;949store950.run_concurrent(async |store| -> wasmtime::Result<()> {951func.call_concurrent(store, ()).await?;952953for _ in 0..5 {954tokio::task::yield_now().await;955}956Ok(())957})958.await??;959960// The host task was cancelled, nothing should remain.961store.assert_concurrent_state_empty();962963Ok(())964}965966#[tokio::test]967#[cfg_attr(miri, ignore)]968async fn sync_lower_async_host_does_not_leak() -> Result<()> {969let mut config = Config::new();970config.wasm_component_model_async(true);971let engine = Engine::new(&config)?;972973let mut store = Store::new(&engine, 0);974let component = Component::new(975&engine,976r#"(component977(import "f" (func $f async))978979(core module $m980(import "" "f" (func $f))981(func (export "run")982(local $c i32)983984;; call the host 100 times985loop986call $f987(local.tee $c (i32.add (local.get $c) (i32.const 1)))988i32.const 100989i32.ne990if br 1 end991end992)993)994(core func $f (canon lower (func $f) ))995(core instance $i (instantiate $m996(with "" (instance997(export "f" (func $f))998))999))10001001(func (export "f") async1002(canon lift (core func $i "run")))100310041005)"#,1006)?;10071008let mut linker = Linker::<usize>::new(&engine);1009linker1010.root()1011.func_wrap_concurrent("f", |accessor, (): ()| {1012Box::pin(async move {1013// Ensure that this doesn't hit the fast path of "ready on1014// first poll"1015for _ in 0..5 {1016tokio::task::yield_now().await;1017}10181019// Keep track of the maximum size of the table in1020// concurrent_state.1021accessor.with(|mut s| {1022let cur = s.as_context_mut().concurrent_state_table_size();1023let max = s.data_mut();1024*max = (*max).max(cur);1025});1026Ok(())1027})1028})?;1029let instance = linker.instantiate_async(&mut store, &component).await?;1030let func = instance.get_typed_func::<(), ()>(&mut store, "f")?;1031func.call_async(&mut store, ()).await?;10321033// First-level assertion: nothing should remain after the guest has exited.1034store.assert_concurrent_state_empty();10351036// Second-level assertion: state should be incrementally cleaned up along1037// the way as the guest calls the host. Things shouldn't leak until the1038// guest exits at the end, for example.1039assert!(1040*store.data() < 100,1041"the store peaked at over 100 items in the concurrent table which \1042indicates that something isn't getting cleaned up between executions \1043of the host"1044);10451046Ok(())1047}104810491050