2019-01-25 15:28:54 -08:00
|
|
|
use crate::{
|
2019-02-22 22:18:59 -08:00
|
|
|
error::{CreationError, GrowError},
|
2019-01-25 15:28:54 -08:00
|
|
|
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,
|
2019-01-25 15:28:54 -08:00
|
|
|
vm,
|
|
|
|
};
|
2019-02-04 15:07:32 -08:00
|
|
|
use std::{
|
2019-02-05 10:20:04 -08:00
|
|
|
cell::{Cell, RefCell},
|
2019-06-11 11:10:42 +02:00
|
|
|
fmt, mem,
|
2019-02-04 15:07:32 -08:00
|
|
|
rc::Rc,
|
|
|
|
};
|
2019-01-25 15:28:54 -08:00
|
|
|
|
|
|
|
pub use self::dynamic::DynamicMemory;
|
|
|
|
pub use self::static_::{SharedStaticMemory, StaticMemory};
|
2019-02-04 23:07:58 -08:00
|
|
|
pub use self::view::{Atomically, MemoryView};
|
2019-01-25 15:28:54 -08:00
|
|
|
|
|
|
|
mod dynamic;
|
2019-06-11 11:10:42 +02:00
|
|
|
pub mod ptr;
|
2019-01-25 15:28:54 -08:00
|
|
|
mod static_;
|
2019-02-04 23:07:58 -08:00
|
|
|
mod view;
|
2019-01-25 15:28:54 -08:00
|
|
|
|
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-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
|
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-01-29 14:15:59 -08:00
|
|
|
/// # fn create_memory() -> Result<()> {
|
|
|
|
/// let descriptor = MemoryDescriptor {
|
2019-01-29 15:44:15 -08:00
|
|
|
/// minimum: Pages(10),
|
2019-01-29 14:15:59 -08:00
|
|
|
/// maximum: None,
|
|
|
|
/// shared: false,
|
|
|
|
/// };
|
2019-01-29 15:44:15 -08:00
|
|
|
///
|
2019-02-04 23:07:58 -08:00
|
|
|
/// let memory = Memory::new(descriptor)?;
|
2019-01-29 14:15:59 -08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2019-02-04 23:07:58 -08:00
|
|
|
pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
|
2019-02-15 13:14:42 -08:00
|
|
|
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-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
|
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 {
|
2019-01-25 15:28:54 -08:00
|
|
|
self.desc
|
|
|
|
}
|
|
|
|
|
2019-02-22 22:18:59 -08:00
|
|
|
/// 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-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
|
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-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
|
2019-02-05 10:20:04 -08:00
|
|
|
/// Return a "view" of the currently accessible memory. By
|
|
|
|
/// default, the view is unsyncronized, using regular memory
|
|
|
|
/// 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::sync::atomic::Ordering;
|
|
|
|
/// # fn view_memory(memory: Memory) {
|
|
|
|
/// // Without synchronization.
|
|
|
|
/// let view: MemoryView<u8> = memory.view();
|
|
|
|
/// for byte in view[0x1000 .. 0x1010].iter().map(|cell| cell.get()) {
|
|
|
|
/// 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-05 10:20:04 -08:00
|
|
|
/// Convert this memory to a shared memory if the shared flag
|
|
|
|
/// is present in the description used to create it.
|
2019-02-04 23:07:58 -08:00
|
|
|
pub fn shared(self) -> Option<SharedMemory> {
|
|
|
|
if self.desc.shared {
|
|
|
|
Some(SharedMemory { desc: self.desc })
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
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(),
|
2019-02-05 10:20:04 -08:00
|
|
|
MemoryVariant::Shared(_) => unimplemented!(),
|
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-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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()
|
2019-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
pub enum MemoryType {
|
|
|
|
Dynamic,
|
|
|
|
Static,
|
|
|
|
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,
|
2019-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[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-01-25 15:28:54 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 15:07:32 -08:00
|
|
|
enum UnsharedMemoryStorage {
|
|
|
|
Dynamic(Box<DynamicMemory>),
|
|
|
|
Static(Box<StaticMemory>),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UnsharedMemory {
|
|
|
|
internal: Rc<UnsharedMemoryInternal>,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct UnsharedMemoryInternal {
|
|
|
|
storage: RefCell<UnsharedMemoryStorage>,
|
|
|
|
local: Cell<vm::LocalMemory>,
|
|
|
|
}
|
|
|
|
|
2019-02-04 23:07:58 -08:00
|
|
|
impl UnsharedMemory {
|
|
|
|
pub fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
|
2019-02-04 15:07:32 -08:00
|
|
|
let mut local = vm::LocalMemory {
|
2019-06-11 11:10:42 +02:00
|
|
|
base: std::ptr::null_mut(),
|
2019-02-04 15:07:32 -08:00
|
|
|
bound: 0,
|
2019-06-11 11:10:42 +02:00
|
|
|
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)?)
|
|
|
|
}
|
|
|
|
MemoryType::SharedStatic => panic!("attempting to create shared unshared memory"),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(UnsharedMemory {
|
|
|
|
internal: Rc::new(UnsharedMemoryInternal {
|
|
|
|
storage: RefCell::new(storage),
|
|
|
|
local: Cell::new(local),
|
|
|
|
}),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-02-22 22:18:59 -08:00
|
|
|
pub fn grow(&self, delta: Pages) -> Result<Pages, GrowError> {
|
2019-02-04 15:07:32 -08:00
|
|
|
let mut storage = self.internal.storage.borrow_mut();
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2019-02-04 23:07:58 -08:00
|
|
|
pub fn size(&self) -> Pages {
|
2019-02-04 15:07:32 -08:00
|
|
|
let storage = self.internal.storage.borrow();
|
|
|
|
|
|
|
|
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: Rc::clone(&self.internal),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-04 23:07:58 -08:00
|
|
|
pub struct SharedMemory {
|
2019-03-15 09:09:24 +01:00
|
|
|
#[allow(dead_code)]
|
2019-02-04 23:07:58 -08:00
|
|
|
desc: MemoryDescriptor,
|
|
|
|
}
|
2019-02-04 15:07:32 -08:00
|
|
|
|
2019-02-04 23:07:58 -08:00
|
|
|
impl SharedMemory {
|
|
|
|
fn new(desc: MemoryDescriptor) -> Result<Self, CreationError> {
|
|
|
|
Ok(Self { desc })
|
2019-02-04 15:07:32 -08:00
|
|
|
}
|
|
|
|
|
2019-02-22 22:18:59 -08:00
|
|
|
pub fn grow(&self, _delta: Pages) -> Result<Pages, GrowError> {
|
2019-02-04 15:07:32 -08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
2019-02-04 23:07:58 -08:00
|
|
|
pub fn size(&self) -> Pages {
|
2019-02-04 15:07:32 -08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Clone for SharedMemory {
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
}
|
2019-02-22 13:16:15 -06:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod memory_tests {
|
|
|
|
|
|
|
|
use super::{Memory, MemoryDescriptor, Pages};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_initial_memory_size() {
|
|
|
|
let unshared_memory = Memory::new(MemoryDescriptor {
|
|
|
|
minimum: Pages(10),
|
|
|
|
maximum: Some(Pages(20)),
|
|
|
|
shared: false,
|
|
|
|
})
|
|
|
|
.unwrap();
|
|
|
|
assert_eq!(unshared_memory.size(), Pages(10));
|
|
|
|
}
|
|
|
|
|
2019-08-13 13:04:13 -06:00
|
|
|
#[test]
|
|
|
|
fn test_invalid_descriptor_returns_error() {
|
|
|
|
let result = Memory::new(MemoryDescriptor {
|
|
|
|
minimum: Pages(10),
|
|
|
|
maximum: None,
|
|
|
|
shared: true,
|
|
|
|
});
|
|
|
|
assert!(
|
|
|
|
result.is_err(),
|
|
|
|
"Max number of pages is required for shared memory"
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-02-22 13:16:15 -06:00
|
|
|
}
|