use crate::memory::{validate_atomic_addr, MmapMemory};
use crate::threads::parking_spot::{ParkingSpot, Waiter};
use crate::vmcontext::VMMemoryDefinition;
use crate::{Memory, RuntimeLinearMemory, Store, WaitResult};
use anyhow::Error;
use anyhow::{bail, Result};
use std::cell::RefCell;
use std::ops::Range;
use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
use std::sync::{Arc, RwLock};
use std::time::Instant;
use wasmtime_environ::{MemoryPlan, MemoryStyle, Trap};
#[derive(Clone)]
pub struct SharedMemory(Arc<SharedMemoryInner>);
struct SharedMemoryInner {
memory: RwLock<Box<dyn RuntimeLinearMemory>>,
spot: ParkingSpot,
ty: wasmtime_environ::Memory,
def: LongTermVMMemoryDefinition,
}
impl SharedMemory {
pub fn new(plan: MemoryPlan) -> Result<Self> {
let (minimum_bytes, maximum_bytes) = Memory::limit_new(&plan, None)?;
let mmap_memory = MmapMemory::new(&plan, minimum_bytes, maximum_bytes, None)?;
Self::wrap(&plan, Box::new(mmap_memory), plan.memory)
}
pub fn wrap(
plan: &MemoryPlan,
mut memory: Box<dyn RuntimeLinearMemory>,
ty: wasmtime_environ::Memory,
) -> Result<Self> {
if !ty.shared {
bail!("shared memory must have a `shared` memory type");
}
if !matches!(plan.style, MemoryStyle::Static { .. }) {
bail!("shared memory can only be built from a static memory allocation")
}
assert!(
memory.as_any_mut().type_id() != std::any::TypeId::of::<SharedMemory>(),
"cannot re-wrap a shared memory"
);
Ok(Self(Arc::new(SharedMemoryInner {
ty,
spot: ParkingSpot::default(),
def: LongTermVMMemoryDefinition(memory.vmmemory()),
memory: RwLock::new(memory),
})))
}
pub fn ty(&self) -> wasmtime_environ::Memory {
self.0.ty
}
pub fn as_memory(self) -> Memory {
Memory(Box::new(self))
}
pub fn vmmemory_ptr(&self) -> *const VMMemoryDefinition {
&self.0.def.0
}
pub fn grow(
&self,
delta_pages: u64,
store: Option<&mut dyn Store>,
) -> Result<Option<(usize, usize)>, Error> {
let mut memory = self.0.memory.write().unwrap();
let result = memory.grow(delta_pages, store)?;
if let Some((_old_size_in_bytes, new_size_in_bytes)) = result {
self.0
.def
.0
.current_length
.store(new_size_in_bytes, Ordering::SeqCst);
}
Ok(result)
}
pub fn atomic_notify(&self, addr_index: u64, count: u32) -> Result<u32, Trap> {
let ptr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
log::trace!("memory.atomic.notify(addr={addr_index:#x}, count={count})");
let ptr = unsafe { &*ptr };
Ok(self.0.spot.notify(ptr, count))
}
pub fn atomic_wait32(
&self,
addr_index: u64,
expected: u32,
timeout: Option<Instant>,
) -> Result<WaitResult, Trap> {
let addr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
log::trace!(
"memory.atomic.wait32(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
);
assert!(std::mem::size_of::<AtomicU32>() == 4);
assert!(std::mem::align_of::<AtomicU32>() <= 4);
let atomic = unsafe { AtomicU32::from_ptr(addr.cast()) };
WAITER.with(|waiter| {
let mut waiter = waiter.borrow_mut();
Ok(self.0.spot.wait32(atomic, expected, timeout, &mut waiter))
})
}
pub fn atomic_wait64(
&self,
addr_index: u64,
expected: u64,
timeout: Option<Instant>,
) -> Result<WaitResult, Trap> {
let addr = validate_atomic_addr(&self.0.def.0, addr_index, 8, 8)?;
log::trace!(
"memory.atomic.wait64(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
);
assert!(std::mem::size_of::<AtomicU64>() == 8);
assert!(std::mem::align_of::<AtomicU64>() <= 8);
let atomic = unsafe { AtomicU64::from_ptr(addr.cast()) };
WAITER.with(|waiter| {
let mut waiter = waiter.borrow_mut();
Ok(self.0.spot.wait64(atomic, expected, timeout, &mut waiter))
})
}
}
thread_local! {
static WAITER: RefCell<Waiter> = const { RefCell::new(Waiter::new()) };
}
struct LongTermVMMemoryDefinition(VMMemoryDefinition);
unsafe impl Send for LongTermVMMemoryDefinition {}
unsafe impl Sync for LongTermVMMemoryDefinition {}
impl RuntimeLinearMemory for SharedMemory {
fn byte_size(&self) -> usize {
self.0.memory.read().unwrap().byte_size()
}
fn maximum_byte_size(&self) -> Option<usize> {
self.0.memory.read().unwrap().maximum_byte_size()
}
fn grow(
&mut self,
delta_pages: u64,
store: Option<&mut dyn Store>,
) -> Result<Option<(usize, usize)>, Error> {
SharedMemory::grow(self, delta_pages, store)
}
fn grow_to(&mut self, size: usize) -> Result<()> {
self.0.memory.write().unwrap().grow_to(size)
}
fn vmmemory(&mut self) -> VMMemoryDefinition {
unreachable!()
}
fn needs_init(&self) -> bool {
self.0.memory.read().unwrap().needs_init()
}
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
self
}
fn wasm_accessible(&self) -> Range<usize> {
self.0.memory.read().unwrap().wasm_accessible()
}
}