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?
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.
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.
the Store is always passed to the wasmtime as &mut to guarantee unique access.
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<...>>
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.
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.
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