diff --git a/lib/api/src/js/mod.rs b/lib/api/src/js/mod.rs index a172dffcf..13cc7a250 100644 --- a/lib/api/src/js/mod.rs +++ b/lib/api/src/js/mod.rs @@ -73,7 +73,6 @@ 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; } diff --git a/lib/api/src/sys/imports.rs b/lib/api/src/sys/imports.rs index c9a9f22b9..b6864e5ee 100644 --- a/lib/api/src/sys/imports.rs +++ b/lib/api/src/sys/imports.rs @@ -1,11 +1,12 @@ //! The import module contains the implementation data structures and helper functions used to //! manipulate and access a wasm module's imports including memories, tables, globals, and //! functions. -use crate::{Exports, Extern, Module}; +use crate::{AsStoreMut, Exports, Extern, Memory, Module}; use std::collections::HashMap; use std::fmt; use wasmer_compiler::LinkError; use wasmer_types::ImportError; +use wasmer_vm::VMSharedMemory; /// All of the import data used when instantiating. /// @@ -111,6 +112,36 @@ impl Imports { .insert((ns.to_string(), name.to_string()), val.into()); } + /// Imports (any) shared memory into the imports. + /// (if the module does not import memory then this function is ignored) + pub fn import_shared_memory( + &mut self, + module: &Module, + store: &mut impl AsStoreMut, + ) -> Option { + // Determine if shared memory needs to be created and imported + let shared_memory = module + .imports() + .memories() + .next() + .map(|a| *a.ty()) + .map(|ty| { + let style = store.as_store_ref().tunables().memory_style(&ty); + VMSharedMemory::new(&ty, &style).unwrap() + }); + + if let Some(memory) = shared_memory { + self.define( + "env", + "memory", + Memory::new_from_existing(store, memory.clone().into()), + ); + Some(memory) + } else { + None + } + } + /// Returns the contents of a namespace as an `Exports`. /// /// Returns `None` if the namespace doesn't exist. diff --git a/lib/api/src/sys/mod.rs b/lib/api/src/sys/mod.rs index d24be112d..c8408b662 100644 --- a/lib/api/src/sys/mod.rs +++ b/lib/api/src/sys/mod.rs @@ -57,8 +57,8 @@ pub mod vm { //! The `vm` module re-exports wasmer-vm types. pub use wasmer_vm::{ - MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable, - VMTableDefinition, + MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, + VMOwnedMemory, VMSharedMemory, VMTable, VMTableDefinition, }; } diff --git a/lib/cli/src/commands/run/wasi.rs b/lib/cli/src/commands/run/wasi.rs index ffc70c420..d913e5119 100644 --- a/lib/cli/src/commands/run/wasi.rs +++ b/lib/cli/src/commands/run/wasi.rs @@ -104,7 +104,8 @@ impl Wasi { is_wasix_module(module), std::sync::atomic::Ordering::Release, ); - let import_object = import_object_for_all_wasi_versions(store, &wasi_env.env); + let mut import_object = import_object_for_all_wasi_versions(store, &wasi_env.env); + import_object.import_shared_memory(module, store); let instance = Instance::new(store, module, &import_object)?; let memory = instance.exports.get_memory("memory")?; wasi_env.data_mut(store).set_memory(memory.clone()); diff --git a/lib/compiler-cranelift/src/translator/code_translator.rs b/lib/compiler-cranelift/src/translator/code_translator.rs index c750d6c32..e38640fb3 100644 --- a/lib/compiler-cranelift/src/translator/code_translator.rs +++ b/lib/compiler-cranelift/src/translator/code_translator.rs @@ -1063,15 +1063,26 @@ pub fn translate_operator( assert!(builder.func.dfg.value_type(expected) == implied_ty); // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what // code it needs to generate, if it wants. - let res = environ.translate_atomic_wait( + match environ.translate_atomic_wait( builder.cursor(), heap_index, heap, addr, expected, timeout, - )?; - state.push1(res); + ) { + Ok(res) => { + state.push1(res); + } + Err(wasmer_types::WasmError::Unsupported(_err)) => { + // If multiple threads hit a mutex then the function will fail + builder.ins().trap(ir::TrapCode::UnreachableCodeReached); + state.reachable = false; + } + Err(err) => { + return Err(err); + } + }; } Operator::MemoryAtomicNotify { memarg } => { let heap_index = MemoryIndex::from_u32(memarg.memory); @@ -1079,9 +1090,20 @@ pub fn translate_operator( let count = state.pop1(); // 32 (fixed) let addr = state.pop1(); // 32 (fixed) let addr = fold_atomic_mem_addr(addr, memarg, I32, builder); - let res = - environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; - state.push1(res); + match environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count) { + Ok(res) => { + state.push1(res); + } + Err(wasmer_types::WasmError::Unsupported(_err)) => { + // Simple return a zero as this function is needed for the __wasi_init_memory function + // but the equivalent notify.wait will not be called (as only one thread calls __start) + // hence these atomic operations are not needed + state.push1(builder.ins().iconst(I32, i64::from(0))); + } + Err(err) => { + return Err(err); + } + }; } Operator::I32AtomicLoad { memarg } => { translate_atomic_load(I32, I32, memarg, builder, state, environ)? diff --git a/lib/compiler/src/translator/environ.rs b/lib/compiler/src/translator/environ.rs index e172d92b0..fc515a9c7 100644 --- a/lib/compiler/src/translator/environ.rs +++ b/lib/compiler/src/translator/environ.rs @@ -1,7 +1,6 @@ // This file contains code from external sources. // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md use super::state::ModuleTranslationState; -use crate::lib::std::borrow::ToOwned; use crate::lib::std::string::ToString; use crate::lib::std::{boxed::Box, string::String, vec::Vec}; use crate::translate_module; @@ -9,13 +8,13 @@ use crate::wasmparser::{Operator, Range, Type}; use std::convert::{TryFrom, TryInto}; use wasmer_types::entity::PrimaryMap; use wasmer_types::FunctionType; +use wasmer_types::WasmResult; use wasmer_types::{ CustomSectionIndex, DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, GlobalType, ImportIndex, LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, SignatureIndex, TableIndex, TableInitializer, TableType, }; -use wasmer_types::{WasmError, WasmResult}; /// Contains function data: bytecode and its offset in the module. #[derive(Hash)] @@ -254,11 +253,6 @@ impl<'data> ModuleEnvironment<'data> { } pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> { - if memory.shared { - return Err(WasmError::Unsupported( - "shared memories are not supported yet".to_owned(), - )); - } self.module.memories.push(memory); Ok(()) } diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index d6b6e2341..dde412ff3 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -16,10 +16,11 @@ use crate::trap::{catch_traps, Trap, TrapCode}; use crate::vmcontext::{ memory_copy, memory_fill, VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, VMFunctionContext, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, - VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, VMTrampoline, + VMMemoryDefinition, VMMemoryImport, VMSharedSignatureIndex, VMTableDefinition, VMTableImport, + VMTrampoline, }; +use crate::LinearMemory; use crate::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMFunctionBody}; -use crate::{LinearMemory, VMMemoryDefinition}; use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable}; pub use allocator::InstanceAllocator; use memoffset::offset_of; diff --git a/lib/vm/src/lib.rs b/lib/vm/src/lib.rs index 1aaae7b52..8d5602fef 100644 --- a/lib/vm/src/lib.rs +++ b/lib/vm/src/lib.rs @@ -45,7 +45,7 @@ pub use crate::function_env::VMFunctionEnvironment; pub use crate::global::*; pub use crate::imports::Imports; pub use crate::instance::{InstanceAllocator, InstanceHandle}; -pub use crate::memory::{initialize_memory_with_data, LinearMemory, VMMemory}; +pub use crate::memory::{initialize_memory_with_data, LinearMemory, VMMemory, VMOwnedMemory, VMSharedMemory}; pub use crate::mmap::Mmap; pub use crate::probestack::PROBESTACK; pub use crate::sig_registry::SignatureRegistry; diff --git a/lib/vm/src/memory.rs b/lib/vm/src/memory.rs index 5a7ea5dad..99d69d9e3 100644 --- a/lib/vm/src/memory.rs +++ b/lib/vm/src/memory.rs @@ -12,6 +12,7 @@ use std::cell::UnsafeCell; use std::convert::TryInto; use std::ptr::NonNull; use std::slice; +use std::sync::{Arc, RwLock}; use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages}; // The memory mapped area @@ -156,6 +157,18 @@ pub struct VMOwnedMemory { unsafe impl Send for VMOwnedMemory {} unsafe impl Sync for VMOwnedMemory {} +/// A shared linear memory instance. +#[derive(Debug, Clone)] +pub struct VMSharedMemory { + // The underlying allocation. + mmap: Arc>, + // Configuration of this memory + config: VMMemoryConfig, +} + +unsafe impl Send for VMSharedMemory {} +unsafe impl Sync for VMSharedMemory {} + impl VMOwnedMemory { /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// @@ -259,6 +272,16 @@ impl VMOwnedMemory { } } +impl VMOwnedMemory { + /// Converts this owned memory into shared memory + pub fn to_shared(self) -> VMSharedMemory { + VMSharedMemory { + mmap: Arc::new(RwLock::new(self.mmap)), + config: self.config, + } + } +} + impl LinearMemory for VMOwnedMemory { /// Returns the type for this memory. fn ty(&self) -> MemoryType { @@ -295,12 +318,85 @@ impl LinearMemory for VMOwnedMemory { } } +impl VMSharedMemory { + /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. + /// + /// This creates a `Memory` with owned metadata: this can be used to create a memory + /// that will be imported into Wasm modules. + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { + Ok(VMOwnedMemory::new(memory, style)?.to_shared()) + } + + /// Create a new linear memory instance with specified minimum and maximum number of wasm pages. + /// + /// This creates a `Memory` with metadata owned by a VM, pointed to by + /// `vm_memory_location`: this can be used to create a local memory. + /// + /// # Safety + /// - `vm_memory_location` must point to a valid location in VM memory. + pub unsafe fn from_definition( + memory: &MemoryType, + style: &MemoryStyle, + vm_memory_location: NonNull, + ) -> Result { + Ok(VMOwnedMemory::from_definition(memory, style, vm_memory_location)?.to_shared()) + } +} + +impl LinearMemory for VMSharedMemory { + /// Returns the type for this memory. + fn ty(&self) -> MemoryType { + let minimum = { + let guard = self.mmap.read().unwrap(); + guard.size() + }; + self.config.ty(minimum) + } + + /// Returns the size of hte memory in pages + fn size(&self) -> Pages { + let guard = self.mmap.read().unwrap(); + guard.size() + } + + /// Returns the memory style for this memory. + fn style(&self) -> MemoryStyle { + self.config.style() + } + + /// Grow memory by the specified amount of wasm pages. + /// + /// Returns `None` if memory can't be grown by the specified amount + /// of wasm pages. + fn grow(&mut self, delta: Pages) -> Result { + let mut guard = self.mmap.write().unwrap(); + guard.grow(delta, self.config.clone()) + } + + /// Return a `VMMemoryDefinition` for exposing the memory to compiled wasm code. + fn vmmemory(&self) -> NonNull { + let guard = self.mmap.read().unwrap(); + guard.vm_memory_definition.as_ptr() + } + + /// Owned memory can not be cloned (this will always return None) + fn try_clone(&self) -> Option> { + None + } +} + impl From for VMMemory { fn from(mem: VMOwnedMemory) -> Self { Self(Box::new(mem)) } } +impl From for VMMemory { + fn from(mem: VMSharedMemory) -> Self { + Self(Box::new(mem)) + } +} + /// Represents linear memory that can be either owned or shared #[derive(Debug)] pub struct VMMemory(pub Box); @@ -357,8 +453,12 @@ impl VMMemory { /// /// This creates a `Memory` with owned metadata: this can be used to create a memory /// that will be imported into Wasm modules. - pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { - Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?))) + pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result { + Ok(if memory.shared { + Self(Box::new(VMSharedMemory::new(memory, style)?)) + } else { + Self(Box::new(VMOwnedMemory::new(memory, style)?)) + }) } /// Returns the number of pages in the allocated memory block @@ -377,12 +477,20 @@ impl VMMemory { memory: &MemoryType, style: &MemoryStyle, vm_memory_location: NonNull, - ) -> Result { - Ok(Self(Box::new(VMOwnedMemory::from_definition( - memory, - style, - vm_memory_location, - )?))) + ) -> Result { + Ok(if memory.shared { + Self(Box::new(VMSharedMemory::from_definition( + memory, + style, + vm_memory_location, + )?)) + } else { + Self(Box::new(VMOwnedMemory::from_definition( + memory, + style, + vm_memory_location, + )?)) + }) } /// Creates VMMemory from a custom implementation - the following into implementations diff --git a/lib/vm/src/store.rs b/lib/vm/src/store.rs index ea11d0582..69ed19856 100644 --- a/lib/vm/src/store.rs +++ b/lib/vm/src/store.rs @@ -78,6 +78,11 @@ impl StoreObjects { self.id } + /// Sets the ID of this store + pub fn set_id(&mut self, id: StoreId) { + self.id = id; + } + /// Returns a pair of mutable references from two handles. /// /// Panics if both handles point to the same object.