component model: async host function & embedding support (#5055)
* func_wrap_async typechecks
* func call async
* instantiate_async
* fixes
* async engine creation for tests
* start adding a component model test for async
* fix wrong check for async support, factor out Instance::new_started to an unchecked impl
* tests: wibbles
* component::Linker::func_wrap: replace IntoComponentFunc with directly accepting a closure
We find that this makes the Linker::func_wrap type signature much easier
to read. The IntoComponentFunc abstraction was adding a lot of weight to
"splat" a set of arguments from a tuple of types into individual
arguments to the closure. Additionally, making the StoreContextMut
argument optional, or the Result<return> optional, wasn't very
worthwhile.
* Fixes for the new style of closure required by component::Linker::func_wrap
* future of result of return
* add Linker::instantiate_async and {Typed}Func::post_return_async
* fix fuzzing generator
* note optimisation opportunity
* simplify test
This commit is contained in:
88
tests/all/component_model/async.rs
Normal file
88
tests/all/component_model/async.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use anyhow::Result;
|
||||
use wasmtime::component::*;
|
||||
use wasmtime::{Store, StoreContextMut, Trap, TrapCode};
|
||||
|
||||
/// This is super::func::thunks, except with an async store.
|
||||
#[tokio::test]
|
||||
async fn smoke() -> Result<()> {
|
||||
let component = r#"
|
||||
(component
|
||||
(core module $m
|
||||
(func (export "thunk"))
|
||||
(func (export "thunk-trap") unreachable)
|
||||
)
|
||||
(core instance $i (instantiate $m))
|
||||
(func (export "thunk")
|
||||
(canon lift (core func $i "thunk"))
|
||||
)
|
||||
(func (export "thunk-trap")
|
||||
(canon lift (core func $i "thunk-trap"))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let engine = super::async_engine();
|
||||
let component = Component::new(&engine, component)?;
|
||||
let mut store = Store::new(&engine, ());
|
||||
let instance = Linker::new(&engine)
|
||||
.instantiate_async(&mut store, &component)
|
||||
.await?;
|
||||
|
||||
let thunk = instance.get_typed_func::<(), (), _>(&mut store, "thunk")?;
|
||||
|
||||
thunk.call_async(&mut store, ()).await?;
|
||||
thunk.post_return_async(&mut store).await?;
|
||||
|
||||
let err = instance
|
||||
.get_typed_func::<(), (), _>(&mut store, "thunk-trap")?
|
||||
.call_async(&mut store, ())
|
||||
.await
|
||||
.unwrap_err();
|
||||
assert!(err.downcast::<Trap>()?.trap_code() == Some(TrapCode::UnreachableCodeReached));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle an import function, created using component::Linker::func_wrap_async.
|
||||
#[tokio::test]
|
||||
async fn smoke_func_wrap() -> Result<()> {
|
||||
let component = r#"
|
||||
(component
|
||||
(type $f (func))
|
||||
(import "i" (func $f))
|
||||
|
||||
(core module $m
|
||||
(import "imports" "i" (func $i))
|
||||
(func (export "thunk") call $i)
|
||||
)
|
||||
|
||||
(core func $f (canon lower (func $f)))
|
||||
(core instance $i (instantiate $m
|
||||
(with "imports" (instance
|
||||
(export "i" (func $f))
|
||||
))
|
||||
))
|
||||
(func (export "thunk")
|
||||
(canon lift (core func $i "thunk"))
|
||||
)
|
||||
)
|
||||
"#;
|
||||
|
||||
let engine = super::async_engine();
|
||||
let component = Component::new(&engine, component)?;
|
||||
let mut store = Store::new(&engine, ());
|
||||
let mut linker = Linker::new(&engine);
|
||||
let mut root = linker.root();
|
||||
root.func_wrap_async("i", |_: StoreContextMut<()>, _: ()| {
|
||||
Box::new(async { Ok(()) })
|
||||
})?;
|
||||
|
||||
let instance = linker.instantiate_async(&mut store, &component).await?;
|
||||
|
||||
let thunk = instance.get_typed_func::<(), (), _>(&mut store, "thunk")?;
|
||||
|
||||
thunk.call_async(&mut store, ()).await?;
|
||||
thunk.post_return_async(&mut store).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user