-
Notifications
You must be signed in to change notification settings - Fork 831
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Instance should implement Sync and Send #748
Comments
We just started working on this :) |
After doing more research, it seems that we don't want #[macro_use]
extern crate specs_derive;
use specs::prelude::*;
use std::borrow::BorrowMut;
use std::sync::Mutex;
use wasmer_runtime::{func, imports, instantiate, Ctx, Func, Instance, Value};
use wasmer_runtime_core::{memory::ptr::WasmPtr, types::ValueType};
#[derive(Debug, Component)]
#[storage(VecStorage)]
struct Vel(f32, f32);
#[derive(Debug, Component, Clone, Copy)]
#[storage(VecStorage)]
#[repr(C)]
struct Pos(f32, f32);
unsafe impl ValueType for Pos {}
#[derive(Debug, Component)]
#[storage(VecStorage)]
struct Name(String);
struct Move;
impl<'a> System<'a> for Move {
type SystemData = (
WriteExpect<'a, MoveScripts>,
WriteStorage<'a, Pos>,
ReadStorage<'a, Vel>,
);
fn run(&mut self, (mut ms, mut pos, vel): Self::SystemData) {
for move_script in &mut ms.0 {
let mut guard = move_script.lock().unwrap();
let ms_inner = guard.borrow_mut();
for (pos, vel) in (&mut pos, &vel).join() {
*pos = ms_inner.update_position((pos.0, pos.1, vel.0, vel.1));
}
}
}
}
struct ShoutOut;
impl<'a> System<'a> for ShoutOut {
type SystemData = (ReadStorage<'a, Name>, ReadStorage<'a, Pos>);
fn run(&mut self, (name, pos): Self::SystemData) {
for (name, pos) in (&name, &pos).join() {
println!("{} is at {}, {}!", name.0, pos.0, pos.1);
}
}
}
static MOVE_SCRIPT_1: &'static [u8] = include_bytes!(
"../../specs-scripts/specs-script/target/wasm32-unknown-unknown/release/specs-script.wasm"
);
static MOVE_SCRIPT_2: &'static [u8] = include_bytes!(
"../../specs-scripts/move-script-2/target/wasm32-unknown-unknown/release/move-script-2.wasm"
);
struct ScriptingSystem {
instance: Instance,
}
struct MoveScript {
system: ScriptingSystem,
}
impl MoveScript {
pub fn update_position(&mut self, (arg1, arg2, arg3, arg4): (f32, f32, f32, f32)) -> Pos {
let update_position = self
.system
.instance
.func::<(f32, f32, f32, f32), u64>("update_position")
.unwrap();
let result = update_position.call(arg1, arg2, arg3, arg4).unwrap();
unsafe { std::mem::transmute(result) }
}
}
struct MoveScripts(Vec<Mutex<MoveScript>>);
impl MoveScripts {
pub fn new(move_scripts: Vec<ScriptingSystem>) -> Self {
let mut inner = vec![];
for script in move_scripts.into_iter() {
let ms = MoveScript { system: script };
inner.push(Mutex::new(ms));
}
Self(inner)
}
}
fn main() {
let import_object = imports! {};
let move_instance_1 = instantiate(MOVE_SCRIPT_1, &import_object).unwrap();
let move_instance_2 = instantiate(MOVE_SCRIPT_2, &import_object).unwrap();
let move_scripts = MoveScripts::new(vec![
ScriptingSystem {
instance: move_instance_1,
},
ScriptingSystem {
instance: move_instance_2,
},
]);
let mut world = World::new();
world.add_resource(move_scripts);
let mut dispatcher = DispatcherBuilder::new()
.with(Move, "move", &[])
.with(ShoutOut, "shout_out", &["move"])
.build();
dispatcher.setup(&mut world);
world
.create_entity()
.with(Vel(2.0, 0.))
.with(Pos(0.0, 0.))
.with(Name("Ario".to_string()))
.build();
world
.create_entity()
.with(Vel(4.0, 1.))
.with(Pos(1.6, 1.))
.with(Name("Bario".to_string()))
.build();
world
.create_entity()
.with(Vel(1.5, 2.))
.with(Pos(5.4, -2.))
.with(Name("Cario".to_string()))
.build();
world.create_entity().with(Pos(2., 2.)).build();
for _ in 0..30 {
dispatcher.dispatch_par(&world);
world.maintain();
}
} Attached is the updated specs example posted in amethyst/amethyst#1729 ; but this one only relies on |
Let me expand a bit on this, the reason why we currently think It's maybe possible to fix this by requiring If we can make all methods on One last note, the example above is almost certainly slower. The acquisition of an uncontested mutex shouldn't take long, especially because it's just once per Wasm Instance per System that uses Wasm. But putting it in a mutex made the ownership more complex and we couldn't store the |
Yes, I agree that there is no real need to implement Also, I would imagine the struct WrappedInstance<'a> {
instance: Instance,
cached_function: Func<'a, (f32, f32, f32, f32), u64>,
}
// And then use access the instance through `Mutex<WrappedInstance<'a>>` This might cause borrow checker issues because of the lifetime parameter on 'WrappedInstance`, but I'm sure there's a way around that. |
@caelunshun Yeah, ideally we'd like to be able to share the module (the compiled Wasm) but not anything that has a In the code above I had that but with one layer of indirection and wasn't able to get it work directly. It seems like something that The issue is that the lifetime struct ScriptingSystem<'a> {
instance: Instance,
_tag: PhantomData<'a>,
}
struct MoveScript<'a> {
system: ScriptingSystem<'a>,
cached_function: Func<'a, (f32, f32, f32, f32), u64>,
} Was roughly what I ended up with before and I wasn't able to make it work; maybe I'm missing something obvious though. Edit: I think this is a really important ergonomic point, so I'll look into it more today and come up with a better justification for why I couldn't get it to work |
807: Implement Send for Instance r=MarkMcCaskey a=MarkMcCaskey # Review - [x] Create a short description of the the change in the CHANGELOG.md file Resolves #748 WIP ## List of changes ### Commit 1 - `Global`s use Arc instead of RC - Export `Context` and `FuncPointer` manually implement Send - `ImportObject` uses `Arc<Mutex<HashMap<...>>>` Instead of `Rc<RefCell<HashMap<...>>>`; removed `get_namespace` method in favor of continuation style to deal with locking the Mutex - `Func` manually implements `Send` (TODO: this change needs to be checked in depth) ### Commit 2 - `unsafe impl Send for Export {}` (temporary to allow Memory to be not Send) - RefCell -> Mutex in Global and Table - Rc -> Arc in Table - Namespace's `IsExport`s must be `Send` (Done to avoid touching much more of the code (i.e. `trait IsExport: Send` requires a lot -- maybe this is what we should do though) - Make `Host` and `Wasm` `Func`s Send (manual implementation) - Manual implementation for `LocalTable` and `AnyFunc` ### Commit 3 - rm placeholder `unsafe impl Send for Export {}` - Manual implementation for `LocalBacking` and `ImportBacking` (both seemed to be not Send due to direct ownership of mutable pointers in their containers) - ImportObject's state creator Fn trait object is now ` + Send + Sync + 'static` (required because it's in an Arc) - Manually implement Send for `InstanceInner` because it holds a raw pointer, `LocalBacking` and `ImportBacking` are marked Send separately - Memory: All Rc -> Arc (including unshared memory); All RefCell -> Mutex (including unshared memory) - Manual implementation of Send and Sync on `UnsharedMemoryInternal` - Manual implementation of Send and Sync on `SharedMemoryInternal` - Change `runtime-core::unix::memory::Memory.fd` from `Option<Rc<Rawfd>>` to `Option<Arc<Rawfd>>` (not strictly required for this change because Memory has manual implementations of Send and Sync, but Arc seems more correct here and there's no comment justifying the use of Rc) - Manual implementation of Send for `ImportedFunc` Co-authored-by: Mark McCaskey <[email protected]> Co-authored-by: Mark McCaskey <[email protected]>
|
Really cool work! In Fluence we are trying to use Wasmer in such that we need state of Wasmer to be preserved globally. And thread local isn't suitable because it is difficult to guarantee lack of context switching on our nodes. So it would be great to make new minor release of Wasmer with this feature :). |
To be able to run Wasmer from different threads Instance will need to implement
Sync
andSend
.Related issue: amethyst/amethyst#1729
The text was updated successfully, but these errors were encountered: