I've been experimenting with having a Guest component create a resource that is then accessed by the Host. I'm struggling with what is the correct way for the Host to do that.
Here is my wit definition
package test:guestresource;
interface bootstrap {
resource myresource {
constructor();
dosomething: func();
}
init: func() -> result<myresource>;
}
world guestresource {
export bootstrap;
}
This is the Guest code
use std::cell::RefCell;
use exports::test::guestresource::bootstrap::Guest;
use exports::test::guestresource::bootstrap::GuestMyresource;
use exports::test::guestresource::bootstrap::Myresource;
wit_bindgen::generate!({
world: "guestresource",
path: "../../wit",
});
struct MyresourceImpl {
data: RefCell<Vec<u32>>,
}
impl GuestMyresource for MyresourceImpl {
fn new() -> Self {
MyresourceImpl{
data: RefCell::new(vec![])
}
}
fn dosomething(&self,) {
let mut data = self.data.borrow_mut();
data.push(1);
}
}
export!(Impl);
struct Impl;
impl Guest for Impl {
type Myresource = MyresourceImpl;
fn init() -> Result<Myresource,()> {
let mri = MyresourceImpl::new();
let mr = Myresource::new(mri);
Ok(mr)
}
}
The Host code that loads and instantiates the Guest looks like this
use wasmtime::{component::{Component, Linker}, Config, Engine, Store};
use wasmtime_wasi::{DirPerms, FilePerms, ResourceTable, WasiCtx, WasiCtxBuilder, WasiView};
mod myresource;
pub struct Ctx {
table: ResourceTable,
wasi: WasiCtx,
}
impl WasiView for Ctx {
fn ctx(&mut self) -> &mut WasiCtx {
&mut self.wasi
}
fn table(&mut self) -> &mut ResourceTable {
&mut self.table
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let mut config = Config::new();
config.wasm_component_model(true);
config.async_support(true);
let engine = Engine::new(&config)?;
let mut linker: Linker<Ctx> = Linker::new(&engine);
wasmtime_wasi::add_to_linker_async(&mut linker)?;
let component = Component::from_file(&engine, "./guest.component.wasm")?;
let wasi = WasiCtxBuilder::new()
.preopened_dir(
"./",
"/",
DirPerms::all(),
FilePerms::all(),
).unwrap()
.inherit_stdio()
.build();
let table = ResourceTable::new();
let ctx = Ctx {
wasi: wasi,
table: table,
};
let mut store: Store<Ctx> = Store::new(
&engine,
ctx
);
let pre = myresource::GuestresourcePre::new(linker.instantiate_pre(&component).unwrap())?;
let instance = pre.instantiate_async(&mut store).await?;
let result = instance.test_guestresource_bootstrap().call_init(&mut store).await?;
let ra = result.unwrap();
let gmr = ra.try_into_resource::<myresource::exports::test::guestresource::bootstrap::GuestMyresource>(&mut store)?;
let gm = store.data_mut().table().get(&gmr)?;
gm.call_dosomething(&mut store, ra).await?;
Ok(())
}
With the referenced myresource module simply containing the wit bindgen
wasmtime::component::bindgen!({
path: "./wit",
world: "guestresource",
async: true,
});
The problem I'm running into is with the double borrow on the store
gm.call_dosomething(&mut store, ra).await?;
"cannot borrow store
as mutable more than once at a time"
This maybe more of Rust problem that is showing my novice Rust skills but would appreciate some suggestions on how this show work
It appears to me the mistake here is using the ResourceTable to store the resource. ResourceTable is a way for host code to map a Resource<T> to its T (the table owns the T), but thats not the right model for the way a resource implemented in the guest works.
really, you just want to keep the Myresource
value, and you should put it somewhere that isnt part of the Store - don't put it into a ResourceTable if you can help it
or if you do put it in there, you can Clone to get the value out. Myresource is basically just an integer - its trivial to "clone" one
afaict, theres no real use of having a Resource<Myresource> in your code example, the GuestMyresource struct provides the methods in terms of just Myresource arguments (which is a type alias for ResourceAny)
Thanks Pat that makes sense
Richard Backhouse has marked this topic as resolved.
Last updated: Dec 23 2024 at 12:05 UTC