1
0
mirror of https://github.com/fluencelabs/wasmer synced 2025-04-13 21:06:02 +00:00

382 lines
12 KiB
Rust
Raw Normal View History

//! The memory module contains the implementation data structures and helper functions used to
//! manipulate and access wasm memory.
use crate::{
error::{CreationError, GrowError},
export::Export,
import::IsExport,
memory::dynamic::DYNAMIC_GUARD_SIZE,
memory::static_::{SAFE_STATIC_GUARD_SIZE, SAFE_STATIC_HEAP_SIZE},
2019-02-04 23:07:58 -08:00
types::{MemoryDescriptor, ValueType},
2019-01-29 15:44:15 -08:00
units::Pages,
vm,
};
use std::{cell::Cell, fmt, mem, sync::Arc};
use std::sync::Mutex as StdMutex;
pub use self::dynamic::DynamicMemory;
pub use self::static_::StaticMemory;
2019-02-04 23:07:58 -08:00
pub use self::view::{Atomically, MemoryView};
use parking_lot::Mutex;
mod dynamic;
pub mod ptr;
mod static_;
2019-02-04 23:07:58 -08:00
mod view;
2019-02-04 23:07:58 -08:00
#[derive(Clone)]
enum MemoryVariant {
Unshared(UnsharedMemory),
Shared(SharedMemory),
2019-02-04 15:07:32 -08:00
}
2019-02-05 10:20:04 -08:00
/// A shared or unshared wasm linear memory.
///
/// A `Memory` represents the memory used by a wasm instance.
2019-02-04 23:07:58 -08:00
#[derive(Clone)]
pub struct Memory {
2019-01-29 13:04:42 -08:00
desc: MemoryDescriptor,
2019-02-04 23:07:58 -08:00
variant: MemoryVariant,
}
2019-02-04 23:07:58 -08:00
impl Memory {
2019-01-29 14:15:59 -08:00
/// Create a new `Memory` from a [`MemoryDescriptor`]
2019-01-29 15:44:15 -08:00
///
2019-01-29 14:15:59 -08:00
/// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
2019-01-29 15:44:15 -08:00
///
2019-01-29 14:15:59 -08:00
/// Usage:
2019-01-29 15:44:15 -08:00
///
2019-01-29 14:15:59 -08:00
/// ```
/// # use wasmer_runtime_core::types::MemoryDescriptor;
/// # use wasmer_runtime_core::memory::Memory;
/// # use wasmer_runtime_core::error::Result;
2019-01-29 15:44:15 -08:00
/// # use wasmer_runtime_core::units::Pages;
2019-09-23 11:17:02 +02:00
/// fn create_memory() -> Result<()> {
/// let descriptor = MemoryDescriptor::new(Pages(10), None, false).unwrap();
2019-01-29 15:44:15 -08:00
///
2019-09-23 11:17:02 +02:00
/// let memory = Memory::new(descriptor)?;
/// Ok(())
/// }
2019-01-29 14:15:59 -08:00
/// ```
2019-02-04 23:07:58 -08:00
pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
if let Some(max) = desc.maximum {
if max < desc.minimum {
return Err(CreationError::InvalidDescriptor(
"Max number of memory pages is less than the minimum number of pages"
.to_string(),
));
}
}
2019-08-13 13:04:13 -06:00
if desc.shared && desc.maximum.is_none() {
return Err(CreationError::InvalidDescriptor(
"Max number of pages is required for shared memory".to_string(),
));
}
2019-02-04 23:07:58 -08:00
let variant = if !desc.shared {
MemoryVariant::Unshared(UnsharedMemory::new(desc)?)
} else {
MemoryVariant::Shared(SharedMemory::new(desc)?)
};
Ok(Memory { desc, variant })
}
2019-01-29 14:15:59 -08:00
/// Return the [`MemoryDescriptor`] that this memory
/// was created with.
2019-01-29 15:44:15 -08:00
///
2019-01-29 14:15:59 -08:00
/// [`MemoryDescriptor`]: struct.MemoryDescriptor.html
2019-01-29 13:04:42 -08:00
pub fn descriptor(&self) -> MemoryDescriptor {
self.desc
}
/// Grow this memory by the specified number of pages.
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
2019-02-04 23:07:58 -08:00
match &self.variant {
MemoryVariant::Unshared(unshared_mem) => unshared_mem.grow(delta),
MemoryVariant::Shared(shared_mem) => shared_mem.grow(delta),
}
}
2019-01-29 14:15:59 -08:00
/// The size, in wasm pages, of this memory.
2019-01-29 15:44:15 -08:00
pub fn size(&self) -> Pages {
2019-02-04 23:07:58 -08:00
match &self.variant {
MemoryVariant::Unshared(unshared_mem) => unshared_mem.size(),
MemoryVariant::Shared(shared_mem) => shared_mem.size(),
}
}
2019-02-05 10:20:04 -08:00
/// Return a "view" of the currently accessible memory. By
2020-02-07 12:16:16 -08:00
/// default, the view is unsynchronized, using regular memory
2019-02-05 10:20:04 -08:00
/// accesses. You can force a memory view to use atomic accesses
/// by calling the [`atomically`] method.
///
/// [`atomically`]: memory/struct.MemoryView.html#method.atomically
///
/// # Notes:
///
/// This method is safe (as in, it won't cause the host to crash or have UB),
/// but it doesn't obey rust's rules involving data races, especially concurrent ones.
/// Therefore, if this memory is shared between multiple threads, a single memory
/// location can be mutated concurrently without synchronization.
///
/// # Usage:
///
/// ```
/// # use wasmer_runtime_core::memory::{Memory, MemoryView};
/// # use std::{cell::Cell, sync::atomic::Ordering};
2019-02-05 10:20:04 -08:00
/// # fn view_memory(memory: Memory) {
/// // Without synchronization.
/// let view: MemoryView<u8> = memory.view();
/// for byte in view[0x1000 .. 0x1010].iter().map(Cell::get) {
2019-02-05 10:20:04 -08:00
/// println!("byte: {}", byte);
/// }
///
/// // With synchronization.
/// let atomic_view = view.atomically();
/// for byte in atomic_view[0x1000 .. 0x1010].iter().map(|atom| atom.load(Ordering::SeqCst)) {
/// println!("byte: {}", byte);
/// }
/// # }
/// ```
pub fn view<T: ValueType>(&self) -> MemoryView<T> {
2019-02-05 11:56:36 -08:00
let vm::LocalMemory { base, .. } = unsafe { *self.vm_local_memory() };
2019-02-04 23:07:58 -08:00
2019-02-05 10:20:04 -08:00
let length = self.size().bytes().0 / mem::size_of::<T>();
2019-02-04 23:07:58 -08:00
2019-02-05 10:20:04 -08:00
unsafe { MemoryView::new(base as _, length as u32) }
2019-01-29 12:12:37 -08:00
}
2019-02-04 15:07:32 -08:00
pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory {
2019-02-04 23:07:58 -08:00
match &self.variant {
MemoryVariant::Unshared(unshared_mem) => unshared_mem.vm_local_memory(),
MemoryVariant::Shared(shared_mem) => shared_mem.vm_local_memory(),
2019-02-04 23:07:58 -08:00
}
2019-01-28 10:59:05 -08:00
}
2019-02-04 15:07:32 -08:00
}
2019-01-28 10:59:05 -08:00
2019-02-04 23:07:58 -08:00
impl IsExport for Memory {
2019-02-02 15:58:33 -08:00
fn to_export(&self) -> Export {
2019-02-04 23:07:58 -08:00
Export::Memory(self.clone())
}
}
2019-02-04 23:07:58 -08:00
impl fmt::Debug for Memory {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("Memory")
.field("desc", &self.desc)
.field("size", &self.size())
.finish()
}
}
/// A kind a memory.
2019-09-17 17:42:06 +02:00
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum MemoryType {
/// A dynamic memory.
Dynamic,
/// A static memory.
Static,
/// A shared static memory.
SharedStatic,
}
impl MemoryType {
#[doc(hidden)]
pub fn guard_size(self) -> u64 {
match self {
MemoryType::Dynamic => DYNAMIC_GUARD_SIZE as u64,
2019-02-04 15:07:32 -08:00
MemoryType::Static | MemoryType::SharedStatic => SAFE_STATIC_GUARD_SIZE as u64,
}
}
#[doc(hidden)]
pub fn bounds(self) -> Option<u64> {
match self {
MemoryType::Dynamic => None,
2019-02-04 15:07:32 -08:00
MemoryType::Static | MemoryType::SharedStatic => Some(SAFE_STATIC_HEAP_SIZE as u64),
}
}
}
2019-02-04 15:07:32 -08:00
enum UnsharedMemoryStorage {
Dynamic(Box<DynamicMemory>),
Static(Box<StaticMemory>),
}
/// A reference to an unshared memory.
2019-02-04 15:07:32 -08:00
pub struct UnsharedMemory {
internal: Arc<UnsharedMemoryInternal>,
2019-02-04 15:07:32 -08:00
}
struct UnsharedMemoryInternal {
storage: StdMutex<UnsharedMemoryStorage>,
2019-02-04 15:07:32 -08:00
local: Cell<vm::LocalMemory>,
}
// Manually implemented because UnsharedMemoryInternal uses `Cell` and is used in an Arc;
// this is safe because the lock for storage can be used to protect (seems like a weak reason: PLEASE REVIEW!)
unsafe impl Sync for UnsharedMemoryInternal {}
2019-02-04 23:07:58 -08:00
impl UnsharedMemory {
/// Create a new `UnsharedMemory` from the given memory descriptor.
2019-02-04 23:07:58 -08:00
pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
2019-02-04 15:07:32 -08:00
let mut local = vm::LocalMemory {
base: std::ptr::null_mut(),
2019-02-04 15:07:32 -08:00
bound: 0,
memory: std::ptr::null_mut(),
2019-02-04 15:07:32 -08:00
};
let storage = match desc.memory_type() {
MemoryType::Dynamic => {
UnsharedMemoryStorage::Dynamic(DynamicMemory::new(desc, &mut local)?)
}
MemoryType::Static => {
UnsharedMemoryStorage::Static(StaticMemory::new(desc, &mut local)?)
}
2019-09-17 17:46:36 +02:00
MemoryType::SharedStatic => {
return Err(CreationError::InvalidDescriptor(
"attempting to create shared unshared memory".to_string(),
));
}
2019-02-04 15:07:32 -08:00
};
Ok(Self {
internal: Arc::new(UnsharedMemoryInternal {
storage: StdMutex::new(storage),
2019-02-04 15:07:32 -08:00
local: Cell::new(local),
}),
})
}
/// Try to grow this memory by the given number of delta pages.
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
let mut storage = self.internal.storage.lock().unwrap();
2019-02-04 15:07:32 -08:00
let mut local = self.internal.local.get();
let pages = match &mut *storage {
UnsharedMemoryStorage::Dynamic(dynamic_memory) => {
dynamic_memory.grow(delta, &mut local)
}
UnsharedMemoryStorage::Static(static_memory) => static_memory.grow(delta, &mut local),
};
self.internal.local.set(local);
pages
}
/// Size of this memory in pages.
2019-02-04 23:07:58 -08:00
pub fn size(&self) -> Pages {
let storage = self.internal.storage.lock().unwrap();
2019-02-04 15:07:32 -08:00
match &*storage {
UnsharedMemoryStorage::Dynamic(ref dynamic_memory) => dynamic_memory.size(),
UnsharedMemoryStorage::Static(ref static_memory) => static_memory.size(),
}
}
2019-02-04 23:07:58 -08:00
pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory {
2019-02-04 15:07:32 -08:00
self.internal.local.as_ptr()
}
}
impl Clone for UnsharedMemory {
fn clone(&self) -> Self {
UnsharedMemory {
internal: Arc::clone(&self.internal),
2019-02-04 15:07:32 -08:00
}
}
}
/// A reference to a shared memory.
2019-02-04 23:07:58 -08:00
pub struct SharedMemory {
internal: Arc<SharedMemoryInternal>,
}
/// Data structure for a shared internal memory.
pub struct SharedMemoryInternal {
memory: StdMutex<Box<StaticMemory>>,
local: Cell<vm::LocalMemory>,
lock: Mutex<()>,
2019-02-04 23:07:58 -08:00
}
2019-02-04 15:07:32 -08:00
// Manually implemented because SharedMemoryInternal uses `Cell` and is used in Arc;
// this is safe because of `lock`; accesing `local` without locking `lock` is not safe (Maybe we could put the lock on Local then?)
unsafe impl Sync for SharedMemoryInternal {}
2019-02-04 23:07:58 -08:00
impl SharedMemory {
fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
let mut local = vm::LocalMemory {
base: std::ptr::null_mut(),
bound: 0,
memory: std::ptr::null_mut(),
};
let memory = StaticMemory::new(desc, &mut local)?;
Ok(Self {
internal: Arc::new(SharedMemoryInternal {
memory: StdMutex::new(memory),
local: Cell::new(local),
lock: Mutex::new(()),
}),
})
2019-02-04 15:07:32 -08:00
}
/// Try to grow this memory by the given number of delta pages.
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
let _guard = self.internal.lock.lock();
let mut local = self.internal.local.get();
let mut memory = self.internal.memory.lock().unwrap();
let pages = memory.grow(delta, &mut local);
pages
2019-02-04 15:07:32 -08:00
}
/// Size of this memory in pages.
2019-02-04 23:07:58 -08:00
pub fn size(&self) -> Pages {
let _guard = self.internal.lock.lock();
let memory = self.internal.memory.lock().unwrap();
memory.size()
}
/// Gets a mutable pointer to the `LocalMemory`.
// This function is scary, because the mutex is not locked here
pub(crate) fn vm_local_memory(&self) -> *mut vm::LocalMemory {
self.internal.local.as_ptr()
2019-02-04 15:07:32 -08:00
}
}
impl Clone for SharedMemory {
fn clone(&self) -> Self {
SharedMemory {
internal: Arc::clone(&self.internal),
}
2019-02-04 15:07:32 -08:00
}
}
#[cfg(test)]
mod memory_tests {
use super::{Memory, MemoryDescriptor, Pages};
#[test]
fn test_initial_memory_size() {
2019-09-23 11:17:02 +02:00
let memory_desc = MemoryDescriptor::new(Pages(10), Some(Pages(20)), false).unwrap();
let unshared_memory = Memory::new(memory_desc).unwrap();
assert_eq!(unshared_memory.size(), Pages(10));
}
2019-08-13 13:04:13 -06:00
#[test]
fn test_invalid_descriptor_returns_error() {
2019-09-23 11:17:02 +02:00
let memory_desc = MemoryDescriptor::new(Pages(10), None, true);
2019-08-13 13:04:13 -06:00
assert!(
2019-09-23 11:17:02 +02:00
memory_desc.is_err(),
2019-08-13 13:04:13 -06:00
"Max number of pages is required for shared memory"
)
}
}