Everything in this question is specific to the Wasmtime runtime for running WebAssembly modules. I am trying to build a Rust program that would be able to do a very basic form of dynamic linking between WebAssembly modules, i.e. providing Rust implementations for host_dlopen(), host_dlsym() functions that would be converted to a WASM Function and be called whenever necessary. My whole problem is being able to correctly (this would be trivial in another language but turns out to be very hard in Rust) pass the Linker into the host_dlopen() I am trying to define.
I have this struct where I defined all objects of interest:
struct GlobalWasmCtx {
engine: Engine,
store: Store<WasiP1Ctx>,
linker: Linker<WasiP1Ctx>,
}
this constructor:
impl GlobalWasmCtx {
fn new() -> Self {
let engine = Engine::default();
let wasi = WasiCtxBuilder::new().build_p1();
let mut store = Store::new(&engine, wasi);
let mut linker = Linker::new(&engine);
Self { engine, store, linker }
}
}
and this declaration:
thread_local! {
static GLOBAL_OBJECTS: RefCell<GlobalWasmCtx> = RefCell::new(GlobalWasmCtx::new());
}
and I am running my whole main function inside a wrapper:
GLOBAL_OBJECTS.with(|ctx| {
...
let dlopen_func = Func::wrap(&mut store, |caller: Caller<'_, WasiP1Ctx>, ptr: i32, flags: i32| {
// Use linker here
Ok(())
});
....
})
This is just a start of a very long error trace:
`NonNull<GlobalWasmCtx>` cannot be shared between threads safely [E0277]
Help: within `RefMut<'_, GlobalWasmCtx>`, the trait `Sync` is not implemented for `NonNull<GlobalWasmCtx>`
Note: required because it appears within the type `RefMut<'_, GlobalWasmCtx>`
Note: required for `&RefMut<'_, GlobalWasmCtx>` to implement `std::marker::Send`
Note: required because it's used within this closure
Note: required for `{closure@wasm_launcher.rs:104:50: 104:103}` to implement `IntoFunc<WasiP1Ctx, (Caller<'_, WasiP1Ctx>, i32, i32), std::result::Result<(), anyhow::Error>>`
Note: required by a bound in `wasmtime::Func::wrap`
There are a few blocks like this and each one ends with "cannot be shared between threads safely". If my Linker and Store were not generic over WasiP1Ctx and were something like Linker<()> and Store<()> this code would work. But WasiP1Ctx introduces the constraint of implementing the Sync trait. And I really need to use this type otherwise I have to implement system calls and argument passing on my own.
I have tried other things as well, wrapping my fields in a RefCell and then in a RwLock like so:
store: RwLock<RefCell<Store<WasiP1Ctx>>>,
linker: RwLock<RefCell<Linker<WasiP1Ctx>>>,
, taking the GlobalWasmCtx outside ofRefCell, but nothing seems to work.
I do not know what are my options to make this work. I would have thought that creating the struct inside thread_local!() is enough. I am a beginner in Rust so probably my approach is fundamentally wrong, but I cannot think of another way to do this. Is passing a Linker object inside a closure accepted by Func::wrap doable in Rust?
Func::wraprequiresfuncto be usable on other threads but how you're constructing it is trying to bind a reference to a thread-local from the creating thread, which isn't going to work. You may be able invert your nesting - i.e. doGLOBAL_OBJECTS.withinside your function instead - but you have some...s that imply more is going on.GlobalWasmCtxs per thread? I'm getting the feeling this is the wrong way to go.