Implemented shared memory for Wasmer in preparation for multithreading

Fixed linter

Fixed clippy

Cleaned up some merge leftover
This commit is contained in:
John Sharratt's Shared Account
2022-08-17 17:45:48 +10:00
committed by ptitSeb
parent 0928629051
commit a5f641b4b0
10 changed files with 190 additions and 29 deletions

View File

@@ -73,7 +73,6 @@ pub use crate::js::value::Value as Val;
pub mod vm { pub mod vm {
//! The `vm` module re-exports wasmer-vm types. //! The `vm` module re-exports wasmer-vm types.
pub use crate::js::export::VMMemory; pub use crate::js::export::VMMemory;
} }

View File

@@ -1,11 +1,12 @@
//! The import module contains the implementation data structures and helper functions used to //! 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 //! manipulate and access a wasm module's imports including memories, tables, globals, and
//! functions. //! functions.
use crate::{Exports, Extern, Module}; use crate::{AsStoreMut, Exports, Extern, Memory, Module};
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use wasmer_compiler::LinkError; use wasmer_compiler::LinkError;
use wasmer_types::ImportError; use wasmer_types::ImportError;
use wasmer_vm::VMSharedMemory;
/// All of the import data used when instantiating. /// All of the import data used when instantiating.
/// ///
@@ -111,6 +112,36 @@ impl Imports {
.insert((ns.to_string(), name.to_string()), val.into()); .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<VMSharedMemory> {
// 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 the contents of a namespace as an `Exports`.
/// ///
/// Returns `None` if the namespace doesn't exist. /// Returns `None` if the namespace doesn't exist.

View File

@@ -57,8 +57,8 @@ pub mod vm {
//! The `vm` module re-exports wasmer-vm types. //! The `vm` module re-exports wasmer-vm types.
pub use wasmer_vm::{ pub use wasmer_vm::{
MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition, VMTable, MemoryError, MemoryStyle, TableStyle, VMExtern, VMMemory, VMMemoryDefinition,
VMTableDefinition, VMOwnedMemory, VMSharedMemory, VMTable, VMTableDefinition,
}; };
} }

View File

@@ -104,7 +104,8 @@ impl Wasi {
is_wasix_module(module), is_wasix_module(module),
std::sync::atomic::Ordering::Release, 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 instance = Instance::new(store, module, &import_object)?;
let memory = instance.exports.get_memory("memory")?; let memory = instance.exports.get_memory("memory")?;
wasi_env.data_mut(store).set_memory(memory.clone()); wasi_env.data_mut(store).set_memory(memory.clone());

View File

@@ -1063,26 +1063,48 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
assert!(builder.func.dfg.value_type(expected) == implied_ty); assert!(builder.func.dfg.value_type(expected) == implied_ty);
// `fn translate_atomic_wait` can inspect the type of `expected` to figure out what // `fn translate_atomic_wait` can inspect the type of `expected` to figure out what
// code it needs to generate, if it wants. // code it needs to generate, if it wants.
let res = environ.translate_atomic_wait( match environ.translate_atomic_wait(
builder.cursor(), builder.cursor(),
heap_index, heap_index,
heap, heap,
addr, addr,
expected, expected,
timeout, timeout,
)?; ) {
Ok(res) => {
state.push1(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 } => { Operator::MemoryAtomicNotify { memarg } => {
let heap_index = MemoryIndex::from_u32(memarg.memory); let heap_index = MemoryIndex::from_u32(memarg.memory);
let heap = state.get_heap(builder.func, memarg.memory, environ)?; let heap = state.get_heap(builder.func, memarg.memory, environ)?;
let count = state.pop1(); // 32 (fixed) let count = state.pop1(); // 32 (fixed)
let addr = state.pop1(); // 32 (fixed) let addr = state.pop1(); // 32 (fixed)
let addr = fold_atomic_mem_addr(addr, memarg, I32, builder); let addr = fold_atomic_mem_addr(addr, memarg, I32, builder);
let res = match environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count) {
environ.translate_atomic_notify(builder.cursor(), heap_index, heap, addr, count)?; Ok(res) => {
state.push1(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 } => { Operator::I32AtomicLoad { memarg } => {
translate_atomic_load(I32, I32, memarg, builder, state, environ)? translate_atomic_load(I32, I32, memarg, builder, state, environ)?
} }

View File

@@ -1,7 +1,6 @@
// This file contains code from external sources. // This file contains code from external sources.
// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md // Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md
use super::state::ModuleTranslationState; use super::state::ModuleTranslationState;
use crate::lib::std::borrow::ToOwned;
use crate::lib::std::string::ToString; use crate::lib::std::string::ToString;
use crate::lib::std::{boxed::Box, string::String, vec::Vec}; use crate::lib::std::{boxed::Box, string::String, vec::Vec};
use crate::translate_module; use crate::translate_module;
@@ -9,13 +8,13 @@ use crate::wasmparser::{Operator, Range, Type};
use std::convert::{TryFrom, TryInto}; use std::convert::{TryFrom, TryInto};
use wasmer_types::entity::PrimaryMap; use wasmer_types::entity::PrimaryMap;
use wasmer_types::FunctionType; use wasmer_types::FunctionType;
use wasmer_types::WasmResult;
use wasmer_types::{ use wasmer_types::{
CustomSectionIndex, DataIndex, DataInitializer, DataInitializerLocation, ElemIndex, CustomSectionIndex, DataIndex, DataInitializer, DataInitializerLocation, ElemIndex,
ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, GlobalType, ImportIndex, ExportIndex, FunctionIndex, GlobalIndex, GlobalInit, GlobalType, ImportIndex,
LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, SignatureIndex, TableIndex, LocalFunctionIndex, MemoryIndex, MemoryType, ModuleInfo, SignatureIndex, TableIndex,
TableInitializer, TableType, TableInitializer, TableType,
}; };
use wasmer_types::{WasmError, WasmResult};
/// Contains function data: bytecode and its offset in the module. /// Contains function data: bytecode and its offset in the module.
#[derive(Hash)] #[derive(Hash)]
@@ -254,11 +253,6 @@ impl<'data> ModuleEnvironment<'data> {
} }
pub(crate) fn declare_memory(&mut self, memory: MemoryType) -> WasmResult<()> { 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); self.module.memories.push(memory);
Ok(()) Ok(())
} }

View File

@@ -16,10 +16,11 @@ use crate::trap::{catch_traps, Trap, TrapCode};
use crate::vmcontext::{ use crate::vmcontext::{
memory_copy, memory_fill, VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext, memory_copy, memory_fill, VMBuiltinFunctionsArray, VMCallerCheckedAnyfunc, VMContext,
VMFunctionContext, VMFunctionImport, VMFunctionKind, VMGlobalDefinition, VMGlobalImport, 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::{FunctionBodyPtr, MaybeInstanceOwned, TrapHandlerFn, VMFunctionBody};
use crate::{LinearMemory, VMMemoryDefinition};
use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable}; use crate::{VMFuncRef, VMFunction, VMGlobal, VMMemory, VMTable};
pub use allocator::InstanceAllocator; pub use allocator::InstanceAllocator;
use memoffset::offset_of; use memoffset::offset_of;

View File

@@ -45,7 +45,7 @@ pub use crate::function_env::VMFunctionEnvironment;
pub use crate::global::*; pub use crate::global::*;
pub use crate::imports::Imports; pub use crate::imports::Imports;
pub use crate::instance::{InstanceAllocator, InstanceHandle}; 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::mmap::Mmap;
pub use crate::probestack::PROBESTACK; pub use crate::probestack::PROBESTACK;
pub use crate::sig_registry::SignatureRegistry; pub use crate::sig_registry::SignatureRegistry;

View File

@@ -12,6 +12,7 @@ use std::cell::UnsafeCell;
use std::convert::TryInto; use std::convert::TryInto;
use std::ptr::NonNull; use std::ptr::NonNull;
use std::slice; use std::slice;
use std::sync::{Arc, RwLock};
use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages}; use wasmer_types::{Bytes, MemoryError, MemoryStyle, MemoryType, Pages};
// The memory mapped area // The memory mapped area
@@ -156,6 +157,18 @@ pub struct VMOwnedMemory {
unsafe impl Send for VMOwnedMemory {} unsafe impl Send for VMOwnedMemory {}
unsafe impl Sync for VMOwnedMemory {} unsafe impl Sync for VMOwnedMemory {}
/// A shared linear memory instance.
#[derive(Debug, Clone)]
pub struct VMSharedMemory {
// The underlying allocation.
mmap: Arc<RwLock<WasmMmap>>,
// Configuration of this memory
config: VMMemoryConfig,
}
unsafe impl Send for VMSharedMemory {}
unsafe impl Sync for VMSharedMemory {}
impl VMOwnedMemory { impl VMOwnedMemory {
/// Create a new linear memory instance with specified minimum and maximum number of wasm pages. /// 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 { impl LinearMemory for VMOwnedMemory {
/// Returns the type for this memory. /// Returns the type for this memory.
fn ty(&self) -> MemoryType { 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<Self, MemoryError> {
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<VMMemoryDefinition>,
) -> Result<Self, MemoryError> {
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<Pages, MemoryError> {
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<VMMemoryDefinition> {
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<Box<dyn LinearMemory + 'static>> {
None
}
}
impl From<VMOwnedMemory> for VMMemory { impl From<VMOwnedMemory> for VMMemory {
fn from(mem: VMOwnedMemory) -> Self { fn from(mem: VMOwnedMemory) -> Self {
Self(Box::new(mem)) Self(Box::new(mem))
} }
} }
impl From<VMSharedMemory> for VMMemory {
fn from(mem: VMSharedMemory) -> Self {
Self(Box::new(mem))
}
}
/// Represents linear memory that can be either owned or shared /// Represents linear memory that can be either owned or shared
#[derive(Debug)] #[derive(Debug)]
pub struct VMMemory(pub Box<dyn LinearMemory + 'static>); pub struct VMMemory(pub Box<dyn LinearMemory + 'static>);
@@ -357,8 +453,12 @@ impl VMMemory {
/// ///
/// This creates a `Memory` with owned metadata: this can be used to create a memory /// This creates a `Memory` with owned metadata: this can be used to create a memory
/// that will be imported into Wasm modules. /// that will be imported into Wasm modules.
pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> { pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<VMMemory, MemoryError> {
Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?))) 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 /// Returns the number of pages in the allocated memory block
@@ -377,12 +477,20 @@ impl VMMemory {
memory: &MemoryType, memory: &MemoryType,
style: &MemoryStyle, style: &MemoryStyle,
vm_memory_location: NonNull<VMMemoryDefinition>, vm_memory_location: NonNull<VMMemoryDefinition>,
) -> Result<Self, MemoryError> { ) -> Result<VMMemory, MemoryError> {
Ok(Self(Box::new(VMOwnedMemory::from_definition( Ok(if memory.shared {
Self(Box::new(VMSharedMemory::from_definition(
memory, memory,
style, style,
vm_memory_location, 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 /// Creates VMMemory from a custom implementation - the following into implementations

View File

@@ -78,6 +78,11 @@ impl StoreObjects {
self.id 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. /// Returns a pair of mutable references from two handles.
/// ///
/// Panics if both handles point to the same object. /// Panics if both handles point to the same object.