Stream: wasi

Topic: ✔ Experimenting with a Guest created resource use by the ...


view this post on Zulip Richard Backhouse (Aug 05 2024 at 11:37):

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

view this post on Zulip Pat Hickey (Aug 05 2024 at 17:14):

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.

view this post on Zulip Pat Hickey (Aug 05 2024 at 17:21):

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

view this post on Zulip Pat Hickey (Aug 05 2024 at 17:22):

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

view this post on Zulip Pat Hickey (Aug 05 2024 at 17:25):

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)

view this post on Zulip Richard Backhouse (Aug 06 2024 at 11:02):

Thanks Pat that makes sense

view this post on Zulip Notification Bot (Aug 06 2024 at 11:02):

Richard Backhouse has marked this topic as resolved.


Last updated: Dec 23 2024 at 12:05 UTC