Stream: wasmtime

Topic: Stuck on how to use WIT Resources


view this post on Zulip Ross Brown (Oct 15 2024 at 06:16):

Are there any examples of implementing a WIT Resource in the host other than at https://component-model.bytecodealliance.org/ ?

I'm trying to have a go but I have got stuck.

I have a Guest function like this:

before-add-note: func(note: borrow<note>);

and a function in my rust based Host application like this:

fn before_add_note(&mut self, note: &mut Note)

N.B. note must be a ref in the rust function, I cannot change this to an Arc etc.

Ideally I would like to be able to do something like this:

fn before_add_note(&mut self, note: &mut Note) {
    self.wasm_component
        .my_component_interface()
        .call_before_add_note(&mut self.wasm_store, &mut note)
        .unwrap();
}

I understand that this is not possible as shown because note needs to be transformed into a handle for the wasm component to use.

Unfortunately I cannot see how I could put &mut note into a ResourceTable without causing issues with lifetimes.

I guess this leaves me with the less ideal solution of cloning note so that I can put it into a ResourceTable, then copying it back later.
Something like this:

fn before_add_note(&mut self, note: &mut Note) {
    let note_resource = self.wasm_store.data_mut().resource_table.push(note.clone()).unwrap();

    self.wasm_component
        .my_component_interface()
        .call_before_add_note(&mut self.wasm_store, note_resource)
        .unwrap();

    *note = self.wasm_store.data_mut().resource_table.delete(note_resource).unwrap();
}

But this does not work either.
Resource<T> is not Copy or Clone and the generated call_before_add_note wants note: Resource<Note>.
This means resource is moved when I call call_before_add_note and I cannot use it with ResourceTable::delete later.

I am not sure what I am meant to be doing at this point.

view this post on Zulip Christof Petig (Oct 15 2024 at 06:39):

This sounds more specific than being solved by a simple example, but have you seen https://github.com/cpetig/resource-demo/blob/main/host-wasmtime/src/main.rs ?

I tested resources on both sides in this repo. Another option could be the runtime tests in wit-bindgen, https://github.com/bytecodealliance/wit-bindgen/blob/main/tests/runtime/resource_import_and_export.rs

Also we use resources in veloren plugins, https://gitlab.com/veloren/veloren/-/blob/master/common/state/src/plugin/module.rs?ref_type=heads but I think they are mostly guest side.

Perhaps these examples already help, otherwise I will need to take a thorough read of your problem statement above (after work).

A demo showing WASM component model resources in various environments - cpetig/resource-demo
A language binding generator for WebAssembly interface types - bytecodealliance/wit-bindgen
Veloren is a multiplayer voxel RPG written in Rust. It is inspired by games such as Cube World, Legend of Zelda: Breath of the Wild, Dwarf Fortress and...

view this post on Zulip Christof Petig (Oct 15 2024 at 06:46):

I hope, ResourceTable as shown in the first link combined with Arc will solve your problem.

view this post on Zulip Ross Brown (Oct 15 2024 at 06:46):

Thanks for the links, I'll have a look.

I assume I'm missing something pretty basic because I currently cannot see how the Host can maintain ownership of any Resources.

view this post on Zulip Ross Brown (Oct 15 2024 at 07:45):

I've had a look at those links but I still cannot see the correct way of doing it.

If I wrapped the cloned Note in an Arc before pushing it into the ResourceTable I would be able access to the Note through the Arc later, but it would still be stuck in the ResourceTable forever.
I would not be able to call ResourceTable::delete to clean up because note_resource will have been moved.

view this post on Zulip Ross Brown (Oct 15 2024 at 10:43):

I made a minimal example of what I am trying to do:

https://github.com/rbrownwsws/wit-host-resources

You can run it with:

cd <repo_root>/guest
cargo component build --release
cd <repo_root>/host
cargo run

I also realised wrapping Note in Arc would not work because the generated call_before_add_note only accepts Resource<Note>, not Resource<Arc<Note>>.

Contribute to rbrownwsws/wit-host-resources development by creating an account on GitHub.

view this post on Zulip Alex Crichton (Oct 16 2024 at 21:46):

Currently Wasmtime's documentation is a bit light on examples: this being the main one for host resources.

For this comment though I think the answer is you can use Resource::new_borrow to pass to the function above. In general I think it's a mistake that Wasmtime doesn't have Clone for Resource<T>. I originally thought that was a bad idea but I've come to think this is not the right conclusion any more.

In the wasmtime-wasi crate we have an extension trait for doing this which you can copy to use locally as well if you'd like.

Contribute to rbrownwsws/wit-host-resources development by creating an account on GitHub.
A fast and secure runtime for WebAssembly. Contribute to bytecodealliance/wasmtime development by creating an account on GitHub.

view this post on Zulip Ross Brown (Oct 17 2024 at 01:41):

Thanks for the help @Alex Crichton, that works for me :smile:.

I found Resource::new_borrow, but as I am not familiar with the internals of Wasmtime/ResourceTable I was worried that manually creating one might break things in a non-obvious way.

In a future version of Wasmtime I think it would be nice to make it more obvious what the correct way is e.g.:

view this post on Zulip Alex Crichton (Oct 19 2024 at 14:55):

Yeah threading the needle on these APIs has been really tricky. It's tough to capture all the nuance in Rust as it's not a 100% match to various Rust concepts despite being similar. Not to say the current API is the best by any means! At the very least I think adding .as_borrowed() or similar is a great idea. The other ideas seem reasonable to me but would need some testing to see what it would be like to implement them.


Last updated: Nov 22 2024 at 16:03 UTC