Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion crates/wasmtime/src/runtime/component/concurrent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ use self::error_contexts::GlobalErrorContextRefCount;
use crate::bail_bug;
use crate::component::func::{Func, call_post_return};
use crate::component::{
HasData, HasSelf, Instance, Resource, ResourceTable, ResourceTableError, RuntimeInstance,
HasData, HasSelf, Instance, InstancePre, Resource, ResourceTable, ResourceTableError,
RuntimeInstance,
};
use crate::fiber::{self, StoreFiber, StoreFiberYield};
use crate::hash_set::HashSet;
Expand Down Expand Up @@ -470,6 +471,34 @@ where
})
}

/// Instantiates a component into the current concurrent store.
///
/// This is the [`Accessor`] equivalent of [`InstancePre::instantiate_async`].
/// It is useful for embedders that need to lazily instantiate components
/// while running inside [`StoreContextMut::run_concurrent`], where the
/// current store is available through this accessor rather than as a
/// separate [`StoreContextMut`].
///
/// # Panics
///
/// This method has the same contextual requirements as [`Accessor::with`]:
/// it must only be called while this accessor's store is available through
/// Wasmtime's concurrent runtime.
pub async fn instantiate_async(&self, pre: &InstancePre<T>) -> Result<Instance>
where
T: Send + 'static,
{
let store = tls::get(|vmstore| {
let store = self.token.as_context_mut(vmstore);
// The concurrent runtime makes the store available through TLS
// for every poll of the host future. `InstancePre::instantiate_async`
// needs to hold the store context across await points, but Rust
// cannot express that TLS-scoped lifetime directly.
unsafe { mem::transmute::<StoreContextMut<'_, T>, StoreContextMut<'static, T>>(store) }
});
pre.instantiate_async(store).await
}

/// Returns the getter this accessor is using to project from `T` into
/// `D::Data`.
pub fn getter(&self) -> fn(&mut T) -> D::Data<'_> {
Expand Down
62 changes: 62 additions & 0 deletions tests/all/component_model/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,68 @@ async fn smoke() -> Result<()> {
Ok(())
}

#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn accessor_can_instantiate_pre() -> Result<()> {
let component = r#"
(component
(import "add-one" (func $add_one (param "x" u32) (result u32)))
(core func $add_one (canon lower (func $add_one)))

(core module $m
(import "" "add-one" (func $add_one (param i32) (result i32)))
(func (export "answer") (param i32) (result i32)
local.get 0
call $add_one
call $add_one
i32.const 40
i32.add)
)

(core instance $i (instantiate $m
(with "" (instance
(export "add-one" (func $add_one))
))
))
(func (export "answer") (param "x" u32) (result u32)
(canon lift (core func $i "answer")))
)
"#;

let engine = super::async_engine();
let component = Component::new(&engine, component)?;
let mut linker = Linker::new(&engine);
linker
.root()
.func_wrap("add-one", |_: StoreContextMut<'_, ()>, (x,): (u32,)| {
Ok((x + 1,))
})?;
let pre = linker.instantiate_pre(&component)?;
let mut store = Store::new(&engine, ());

let (answer_a, answer_b) = store
.run_concurrent(async |accessor| -> Result<_> {
let instance_a = accessor.instantiate_async(&pre).await?;
let instance_b = accessor.instantiate_async(&pre).await?;
let (answer_a, answer_b) = accessor.with(|mut access| {
let answer_a =
instance_a.get_typed_func::<(u32,), (u32,)>(&mut access, "answer")?;
let answer_b =
instance_b.get_typed_func::<(u32,), (u32,)>(&mut access, "answer")?;
Ok::<_, wasmtime::Error>((answer_a, answer_b))
})?;
let (answer_a,) = answer_a.call_concurrent(accessor, (0,)).await?;
let (answer_b,) = answer_b.call_concurrent(accessor, (10,)).await?;
Ok((answer_a, answer_b))
})
.await??;

assert_eq!(answer_a, 42);
assert_eq!(answer_b, 52);

Ok(())
}

/// Handle an import function, created using component::Linker::func_wrap_async.
#[tokio::test]
#[cfg_attr(miri, ignore)]
Expand Down
Loading