mirror of
https://github.com/mii443/wasmer.git
synced 2025-12-07 05:08:19 +00:00
Implemented shared memory for Wasmer in preparation for multithreading
Fixed linter Fixed clippy Cleaned up some merge leftover
This commit is contained in:
committed by
ptitSeb
parent
0928629051
commit
a5f641b4b0
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<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 `None` if the namespace doesn't exist.
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -1063,26 +1063,48 @@ pub fn translate_operator<FE: FuncEnvironment + ?Sized>(
|
||||
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,
|
||||
)?;
|
||||
) {
|
||||
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);
|
||||
let heap = state.get_heap(builder.func, memarg.memory, environ)?;
|
||||
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)?;
|
||||
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)?
|
||||
}
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<RwLock<WasmMmap>>,
|
||||
// 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<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 {
|
||||
fn from(mem: VMOwnedMemory) -> Self {
|
||||
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
|
||||
#[derive(Debug)]
|
||||
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
|
||||
/// that will be imported into Wasm modules.
|
||||
pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<Self, MemoryError> {
|
||||
Ok(Self(Box::new(VMOwnedMemory::new(memory, style)?)))
|
||||
pub fn new(memory: &MemoryType, style: &MemoryStyle) -> Result<VMMemory, MemoryError> {
|
||||
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<VMMemoryDefinition>,
|
||||
) -> Result<Self, MemoryError> {
|
||||
Ok(Self(Box::new(VMOwnedMemory::from_definition(
|
||||
) -> Result<VMMemory, MemoryError> {
|
||||
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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user