Skip to content
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

Multithreading, full networking and RPC for WebAssembly #3116

Merged
merged 4 commits into from
Aug 25, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
267 changes: 189 additions & 78 deletions Cargo.lock

Large diffs are not rendered by default.

5 changes: 2 additions & 3 deletions docs/migration_to_3.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,14 +139,13 @@ import_object.define("env", "host_function", host_function);
let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module.");
```

For WASI, don't forget to import memory to `WasiEnv`
For WASI, don't forget to initialize it

```rust
let mut wasi_env = WasiState::new("hello").finalize()?;
let import_object = wasi_env.import_object(&mut store, &module)?;
let instance = Instance::new(&mut store, &module, &import_object).expect("Could not instantiate module.");
let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(&mut store).set_memory(memory.clone());
wasi_env.initialize(&mut store, &instance).unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we will want to do this change in the 3.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes i think it would be good to bring this forward - i think this way is cleaner

```

#### `ChainableNamedResolver` is removed
Expand Down
4 changes: 2 additions & 2 deletions examples/imports_function_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
fn get_counter(env: FunctionEnvMut<Env>) -> i32 {
*env.data().counter.lock().unwrap()
}
fn add_to_counter(mut env: FunctionEnvMut<Env>, add: i32) -> i32 {
let mut counter_ref = env.data_mut().counter.lock().unwrap();
fn add_to_counter(env: FunctionEnvMut<Env>, add: i32) -> i32 {
let mut counter_ref = env.data().counter.lock().unwrap();

*counter_ref += add;
*counter_ref
Expand Down
9 changes: 4 additions & 5 deletions examples/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

println!("Creating `WasiEnv`...");
// First, we create the `WasiEnv`
let wasi_env = WasiState::new("hello")
let mut wasi_env = WasiState::new("hello")
// .args(&["world"])
// .env("KEY", "Value")
.finalize(&mut store)?;
Expand All @@ -50,10 +50,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let import_object = wasi_env.import_object(&mut store, &module)?;
let instance = Instance::new(&mut store, &module, &import_object)?;

println!("Attach WASI memory...");
// Attach the memory export
let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(&mut store).set_memory(memory.clone());
println!("Initializing WASI environment...");
// Initialize the WASI environment (which will attach memory)
wasi_env.initialize(&mut store, &instance).unwrap();

println!("Call WASI `_start` function...");
// And we just call the `_start` function!
Expand Down
7 changes: 3 additions & 4 deletions examples/wasi_pipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let import_object = wasi_env.import_object(&mut store, &module)?;
let instance = Instance::new(&mut store, &module, &import_object)?;

println!("Attach WASI memory...");
// Attach the memory export
let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(&mut store).set_memory(memory.clone());
println!("Initializing WASI environment...");
// Initialize the WASI environment (which will attach memory)
wasi_env.initialize(&mut store, &instance).unwrap();

let msg = "racecar go zoom";
println!("Writing \"{}\" to the WASI stdin...", msg);
Expand Down
1 change: 1 addition & 0 deletions lib/api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ indexmap = { version = "1.6" }
cfg-if = "1.0"
thiserror = "1.0"
more-asserts = "0.2"
bytes = "1"
# - Optional shared dependencies.
wat = { version = "1.0", optional = true }
tracing = { version = "0.1", optional = true }
Expand Down
9 changes: 8 additions & 1 deletion lib/api/src/js/export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::fmt;
use wasm_bindgen::{JsCast, JsValue};
use wasmer_types::{ExternType, FunctionType, GlobalType, MemoryType, TableType};

/// Represents linear memory that is managed by the javascript runtime
#[derive(Clone, Debug, PartialEq)]
pub struct VMMemory {
pub(crate) memory: Memory,
Expand All @@ -17,9 +18,15 @@ unsafe impl Send for VMMemory {}
unsafe impl Sync for VMMemory {}

impl VMMemory {
pub(crate) fn new(memory: Memory, ty: MemoryType) -> Self {
/// Creates a new memory directly from a WebAssembly javascript object
pub fn new(memory: Memory, ty: MemoryType) -> Self {
Self { memory, ty }
}

/// Attempts to clone this memory (if its clonable)
pub(crate) fn try_clone(&self) -> Option<VMMemory> {
Some(self.clone())
}
}

#[derive(Clone, Debug, PartialEq)]
Expand Down
3 changes: 3 additions & 0 deletions lib/api/src/js/exports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ pub enum ExportError {
/// This error arises when an export is missing
#[error("Missing export {0}")]
Missing(String),
/// This error arises when an export is missing
#[error("Serialization failed {0}")]
SerializationFailed(String),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I understand when this can be triggered.
When an export can be missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this is leftovers - this is not used anymore - we should remove it

}

/// Exports is a special kind of map that allows easily unwrapping
Expand Down
35 changes: 18 additions & 17 deletions lib/api/src/js/externals/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ use crate::js::{MemoryAccessError, MemoryType};
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::slice;
use thiserror::Error;
#[cfg(feature = "tracing")]
use tracing::warn;

Expand All @@ -16,22 +15,7 @@ use wasmer_types::Pages;

use super::MemoryView;

/// Error type describing things that can go wrong when operating on Wasm Memories.
#[derive(Error, Debug, Clone, PartialEq, Hash)]
pub enum MemoryError {
/// The operation would cause the size of the memory to exceed the maximum or would cause
/// an overflow leading to unindexable memory.
#[error("The memory could not grow: current size {} pages, requested increase: {} pages", current.0, attempted_delta.0)]
CouldNotGrow {
/// The current size in pages.
current: Pages,
/// The attempted amount to grow by in pages.
attempted_delta: Pages,
},
/// A user defined error value, used for error cases not listed above.
#[error("A user-defined error occurred: {0}")]
Generic(String),
}
pub use wasmer_types::MemoryError;

#[wasm_bindgen]
extern "C" {
Expand Down Expand Up @@ -116,6 +100,17 @@ impl Memory {
Ok(Self::from_vm_export(store, vm_memory))
}

/// Creates a new host `Memory` from provided JavaScript memory.
pub fn new_raw(store: &mut impl AsStoreMut, js_memory: js_sys::WebAssembly::Memory, ty: MemoryType) -> Result<Self, MemoryError> {
let vm_memory = VMMemory::new(js_memory, ty);
Ok(Self::from_vm_export(store, vm_memory))
}

/// Create a memory object from an existing memory and attaches it to the store
pub fn new_from_existing(new_store: &mut impl AsStoreMut, memory: VMMemory) -> Self {
Self::from_vm_export(new_store, memory)
}

/// Returns the [`MemoryType`] of the `Memory`.
///
/// # Example
Expand Down Expand Up @@ -210,6 +205,12 @@ impl Memory {
}
}

/// Attempts to clone this memory (if its clonable)
pub fn try_clone(&self, store: &impl AsStoreRef) -> Option<VMMemory> {
let mem = self.handle.get(store.as_store_ref().objects());
mem.try_clone()
}

/// Checks whether this `Global` can be used with the given context.
pub fn is_from_store(&self, store: &impl AsStoreRef) -> bool {
self.handle.store_id() == store.as_store_ref().objects().id()
Expand Down
7 changes: 6 additions & 1 deletion lib/api/src/js/function_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<T> FunctionEnv<T> {
}

/// Get the data as reference
pub fn as_ref<'a>(&self, store: &'a impl AsStoreMut) -> &'a T
pub fn as_ref<'a>(&self, store: &'a impl AsStoreRef) -> &'a T
where
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That could also go to 3.0

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah this is a easy one to bring in

T: Any + Send + 'static + Sized,
{
Expand Down Expand Up @@ -112,6 +112,11 @@ impl<T: Send + 'static> FunctionEnvMut<'_, T> {
self.func_env.as_mut(&mut self.store_mut)
}

/// Borrows a new immmutable reference
pub fn as_ref(&self) -> FunctionEnv<T> {
self.func_env.clone()
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And that could go to 3.0 too, I suppose

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah if we move the other ones we might as well

/// Borrows a new mutable reference
pub fn as_mut<'a>(&'a mut self) -> FunctionEnvMut<'a, T> {
FunctionEnvMut {
Expand Down
30 changes: 30 additions & 0 deletions lib/api/src/js/imports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,36 @@ impl Imports {
}
imports
}

/// Iterates through all the imports in this structure
pub fn iter<'a>(&'a self) -> ImportsIterator<'a> {
ImportsIterator::new(self)
}
}

pub struct ImportsIterator<'a> {
iter: std::collections::hash_map::Iter<'a, (String, String), Extern>
}

impl<'a> ImportsIterator<'a>
{
fn new(imports: &'a Imports) -> Self {
let iter = imports.map.iter();
Self { iter }
}
}

impl<'a> Iterator
for ImportsIterator<'a> {
type Item = (&'a str, &'a str, &'a Extern);

fn next(&mut self) -> Option<Self::Item> {
self.iter
.next()
.map(|(k, v)| {
(k.0.as_str(), k.1.as_str(), v)
})
}
}

impl IntoIterator for &Imports {
Expand Down
20 changes: 12 additions & 8 deletions lib/api/src/js/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ use std::fmt;
pub struct Instance {
_handle: StoreHandle<WebAssembly::Instance>,
module: Module,
#[allow(dead_code)]
imports: Imports,
/// The exports for an instance.
pub exports: Exports,
}
Expand Down Expand Up @@ -65,12 +63,11 @@ impl Instance {
module: &Module,
imports: &Imports,
) -> Result<Self, InstantiationError> {
let import_copy = imports.clone();
let (instance, _imports): (StoreHandle<WebAssembly::Instance>, Vec<Extern>) = module
let (instance, externs): (StoreHandle<WebAssembly::Instance>, Vec<Extern>) = module
.instantiate(&mut store, imports)
.map_err(|e| InstantiationError::Start(e))?;

let self_instance = Self::from_module_and_instance(store, module, instance, import_copy)?;
let self_instance = Self::from_module_and_instance(store, module, externs, instance)?;
//self_instance.init_envs(&imports.iter().map(Extern::to_export).collect::<Vec<_>>())?;
Ok(self_instance)
}
Expand All @@ -87,11 +84,11 @@ impl Instance {
pub fn from_module_and_instance(
mut store: &mut impl AsStoreMut,
module: &Module,
externs: Vec<Extern>,
instance: StoreHandle<WebAssembly::Instance>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's another API change that should go in 3.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure on this one as it will drag a bunch of other code with it - might be too much

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does that mean the signature of the function can be rolled back to without the externs, and we forget about this change?

imports: Imports,
) -> Result<Self, InstantiationError> {
let instance_exports = instance.get(store.as_store_ref().objects()).exports();
let exports = module
let mut exports = module
.exports()
.map(|export_type| {
let name = export_type.name();
Expand All @@ -110,10 +107,17 @@ impl Instance {
})
.collect::<Result<Exports, InstantiationError>>()?;

// If the memory is imported then also export it for backwards compatibility reasons
// (many will assume the memory is always exported) - later we can remove this
if exports.get_memory("memory").is_err() {
if let Some(memory) = externs.iter().filter(|a| a.ty(store).memory().is_some()).next() {
exports.insert("memory", memory.clone());
}
}
Comment on lines +110 to +116
Copy link
Member

@syrusakbary syrusakbary Aug 18, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really believe we shouldn't do this on the API layer. This should live somewhere else (not on the JS API layer, as it's not JS or VM specific)

Copy link
Contributor Author

@john-sharratt john-sharratt Aug 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that will trigger some increased coding because of these...

image

But if we are ok to modify the interfaces that assume memory is an export (perhaps we need to put in some form of abstraction) then we can get rid of these.


Ok(Self {
_handle: instance,
module: module.clone(),
imports,
exports,
})
}
Expand Down
6 changes: 6 additions & 0 deletions lib/api/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ pub use crate::js::types::{
pub use crate::js::value::Value;
pub use crate::js::value::Value as Val;

pub mod vm {
//! The `vm` module re-exports wasmer-vm types.

pub use crate::js::export::VMMemory;
}

pub use wasmer_types::is_wasm;
pub use wasmer_types::{
Bytes, ExportIndex, GlobalInit, LocalFunctionIndex, Pages, ValueType, WASM_MAX_PAGES,
Expand Down
Loading