Skip to content

Commit

Permalink
Add the ability to generate async drop methods for resources.
Browse files Browse the repository at this point in the history
In the component model, `resource.drop` is a canonical built-in without a proper name. So I invented a custom naming scheme for the component bindgen config. I went with:
`"[drop]{resource-name}"` where `{resource-name}` is the name as defined in WIT. e.g. `"[drop]input-stream"`.

This shouldn't conflict with anything existing in the wild as WIT identifiers are not allowed to contain square brackets.
  • Loading branch information
badeend committed Aug 8, 2024
1 parent 895180d commit ea91c59
Show file tree
Hide file tree
Showing 4 changed files with 170 additions and 72 deletions.
21 changes: 12 additions & 9 deletions crates/component-macro/tests/expanded/resources-export_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,18 @@ pub mod foo {
pub enum Y {}
#[wasmtime::component::__internal::async_trait]
pub trait HostY {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Y>,
) -> wasmtime::Result<()>;
}
#[wasmtime::component::__internal::async_trait]
impl<_T: HostY + ?Sized + Send> HostY for &mut _T {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Y>,
) -> wasmtime::Result<()> {
HostY::drop(*self, rep)
HostY::drop(*self, rep).await
}
}
#[wasmtime::component::__internal::async_trait]
Expand All @@ -192,14 +192,17 @@ pub mod foo {
T: Send,
{
let mut inst = linker.instance("foo:foo/transitive-import")?;
inst.resource(
inst.resource_async(
"y",
wasmtime::component::ResourceType::host::<Y>(),
move |mut store, rep| -> wasmtime::Result<()> {
HostY::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
move |mut store, rep| {
std::boxed::Box::new(async move {
HostY::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
.await
})
},
)?;
Ok(())
Expand Down
84 changes: 48 additions & 36 deletions crates/component-macro/tests/expanded/resources-import_async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pub trait HostWorldResource {
async fn new(&mut self) -> wasmtime::component::Resource<WorldResource>;
async fn foo(&mut self, self_: wasmtime::component::Resource<WorldResource>) -> ();
async fn static_foo(&mut self) -> ();
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<WorldResource>,
) -> wasmtime::Result<()>;
Expand All @@ -20,11 +20,11 @@ impl<_T: HostWorldResource + ?Sized + Send> HostWorldResource for &mut _T {
async fn static_foo(&mut self) -> () {
HostWorldResource::static_foo(*self).await
}
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<WorldResource>,
) -> wasmtime::Result<()> {
HostWorldResource::drop(*self, rep)
HostWorldResource::drop(*self, rep).await
}
}
/// Auto-generated bindings for a pre-instantiated version of a
Expand Down Expand Up @@ -166,14 +166,17 @@ const _: () = {
{
let mut linker = linker.root();
linker
.resource(
.resource_async(
"world-resource",
wasmtime::component::ResourceType::host::<WorldResource>(),
move |mut store, rep| -> wasmtime::Result<()> {
HostWorldResource::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
move |mut store, rep| {
std::boxed::Box::new(async move {
HostWorldResource::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
.await
})
},
)?;
linker
Expand Down Expand Up @@ -277,7 +280,7 @@ pub mod foo {
&mut self,
self_: wasmtime::component::Resource<Bar>,
) -> u32;
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Bar>,
) -> wasmtime::Result<()>;
Expand All @@ -296,11 +299,11 @@ pub mod foo {
) -> u32 {
HostBar::method_a(*self, self_).await
}
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Bar>,
) -> wasmtime::Result<()> {
HostBar::drop(*self, rep)
HostBar::drop(*self, rep).await
}
}
#[derive(wasmtime::component::ComponentType)]
Expand Down Expand Up @@ -444,14 +447,17 @@ pub mod foo {
T: Send,
{
let mut inst = linker.instance("foo:foo/resources")?;
inst.resource(
inst.resource_async(
"bar",
wasmtime::component::ResourceType::host::<Bar>(),
move |mut store, rep| -> wasmtime::Result<()> {
HostBar::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
move |mut store, rep| {
std::boxed::Box::new(async move {
HostBar::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
.await
})
},
)?;
inst.func_wrap_async(
Expand Down Expand Up @@ -808,18 +814,18 @@ pub mod foo {
pub enum A {}
#[wasmtime::component::__internal::async_trait]
pub trait HostA {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<A>,
) -> wasmtime::Result<()>;
}
#[wasmtime::component::__internal::async_trait]
impl<_T: HostA + ?Sized + Send> HostA for &mut _T {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<A>,
) -> wasmtime::Result<()> {
HostA::drop(*self, rep)
HostA::drop(*self, rep).await
}
}
#[wasmtime::component::__internal::async_trait]
Expand All @@ -844,14 +850,17 @@ pub mod foo {
T: Send,
{
let mut inst = linker.instance("foo:foo/long-use-chain1")?;
inst.resource(
inst.resource_async(
"a",
wasmtime::component::ResourceType::host::<A>(),
move |mut store, rep| -> wasmtime::Result<()> {
HostA::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
move |mut store, rep| {
std::boxed::Box::new(async move {
HostA::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
.await
})
},
)?;
Ok(())
Expand Down Expand Up @@ -1016,18 +1025,18 @@ pub mod foo {
pub enum Foo {}
#[wasmtime::component::__internal::async_trait]
pub trait HostFoo {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Foo>,
) -> wasmtime::Result<()>;
}
#[wasmtime::component::__internal::async_trait]
impl<_T: HostFoo + ?Sized + Send> HostFoo for &mut _T {
fn drop(
async fn drop(
&mut self,
rep: wasmtime::component::Resource<Foo>,
) -> wasmtime::Result<()> {
HostFoo::drop(*self, rep)
HostFoo::drop(*self, rep).await
}
}
#[wasmtime::component::__internal::async_trait]
Expand All @@ -1053,14 +1062,17 @@ pub mod foo {
{
let mut inst = linker
.instance("foo:foo/transitive-interface-with-resource")?;
inst.resource(
inst.resource_async(
"foo",
wasmtime::component::ResourceType::host::<Foo>(),
move |mut store, rep| -> wasmtime::Result<()> {
HostFoo::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
move |mut store, rep| {
std::boxed::Box::new(async move {
HostFoo::drop(
&mut host_getter(store.data_mut()),
wasmtime::component::Resource::new_own(rep),
)
.await
})
},
)?;
Ok(())
Expand Down
31 changes: 31 additions & 0 deletions crates/wasmtime/src/runtime/component/linker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,37 @@ impl<T> LinkerInstance<'_, T> {
Ok(())
}

/// Identical to [`Self::resource`], except that it takes an async destructor.
#[cfg(feature = "async")]
pub fn resource_async<F>(&mut self, name: &str, ty: ResourceType, dtor: F) -> Result<()>
where
F: for<'a> Fn(
StoreContextMut<'a, T>,
u32,
) -> Box<dyn Future<Output = Result<()>> + Send + 'a>
+ Send
+ Sync
+ 'static,
{
assert!(
self.engine.config().async_support,
"cannot use `resource_async` without enabling async support in the config"
);
let dtor = Arc::new(crate::func::HostFunc::wrap_inner(
&self.engine,
move |mut cx: crate::Caller<'_, T>, (param,): (u32,)| {
let async_cx = cx.as_context_mut().0.async_cx().expect("async cx");
let mut future = Pin::from(dtor(cx.as_context_mut(), param));
match unsafe { async_cx.block_on(future.as_mut()) } {
Ok(Ok(())) => Ok(()),
Ok(Err(trap)) | Err(trap) => Err(trap),
}
},
));
self.insert(name, Definition::Resource(ty, dtor))?;
Ok(())
}

/// Defines a nested instance within this instance.
///
/// This can be used to describe arbitrarily nested levels of instances
Expand Down
Loading

0 comments on commit ea91c59

Please sign in to comment.