Stream: general

Topic: wasmtime Rust API architecture question


view this post on Zulip Ian Kettlewell (Aug 12 2023 at 19:59):

I want to have something kinda like this:

struct Context {
    programs: Vec<(Option<wasmtime::Store<Context>>, wasmtime::Instance)>>
}

I'd like to provide a host function that allows calling into any program to get a return value, or read from its memory. What I do right now is take a program's Store from the Option before executing it, but that means that reentrancy into any program in the call-stack is impossible.

What I'm struggling with architecturally is the fact that the Store is mutably borrowed during calls. wasmtime::Caller<'_, Context>is passed into the provided host functions, so I do have access that way, but if calls between programs get deeply nested I can't think of a good way to pass around the temporary reference.

Am I thinking about this the wrong way, or is there something I'm overlooking?

view this post on Zulip Ian Kettlewell (Aug 12 2023 at 20:15):

I can understand how to make it work if host functions could take temporary ownership of the Store, but that's now how the API works.

view this post on Zulip Christof Petig (Aug 13 2023 at 06:53):

For each (?) runtime there is a more complex function api which gives exclusive access to the store from within called functions, at least I have used this from wasmtime, wamr and wasmer.
If you need a more complex setup I fear that you are looking for the stack switching proposal.

view this post on Zulip Pat Hickey (Aug 14 2023 at 15:44):

the Store is always passed to the wasmtime as &mut to guarantee unique access.

view this post on Zulip Pat Hickey (Aug 14 2023 at 15:45):

if you need some sort of non-unique access to data inside the T of Store<T> you'll need to use some other primitive that provides that, e.g. Arc<Mutex<...>>

view this post on Zulip Ian Kettlewell (Aug 14 2023 at 21:17):

The problem I'm facing is that I can't put that &mut Store into another data structure without it also having that lifetime. I could probably use unsafe to ignore the lifetime, but that's not ideal.

Conceptually if the thing passed in were a &mut Option<Store> I could take out of the Option<Store>, put into my data structure and make sure to put it back before returning.

view this post on Zulip Ian Kettlewell (Aug 14 2023 at 21:21):

What I'm trying to accomplish is an architecture where a bunch of Wasm programs can call into and read memory from each-other. Right now I can I make that work in a simple scenario like program A calling B. But if A calls B and then something in the call-stack tries to read from A it's tough to make that work without resorting to unsafe.

view this post on Zulip Alex Crichton (Aug 18 2023 at 14:34):

I think that the API you're trying to achieve is probably nontrivial and difficult to achieve with Wasmtime. One possible idea is to have a ContextTmp<'a> which stores StoreContextMut<'a, Context> instead of Store<Context>. When a callback is invoked you're effectively given StoreContextMut<'a,T> back for the store that was invoked (for recursive calls), so you'd take that and construct a ContextTmp from the outer context information (perhaps threaded through TLS? unsure. That's still pretty gnarly though and probably not that feasible.

I'll note though that I would highly recommend not using unsafe to achieve your goal here. Lots and lots of Wasmtime relies on the actual guarantees of uniqueness for safety and we heavily rely on all of Rust's guarantees. Using unsafe can very easily break many of those assumptions and lead to actual unsafety even if you didn't intend.


Last updated: Dec 23 2024 at 12:05 UTC