wasmtime/runtime/vm/memory/
shared_memory.rs1use crate::Engine;
2use crate::prelude::*;
3use crate::runtime::vm::memory::{LocalMemory, MmapMemory, validate_atomic_addr};
4use crate::runtime::vm::parking_spot::{ParkingSpot, Waiter};
5use crate::runtime::vm::{self, Memory, VMMemoryDefinition, WaitResult};
6use std::cell::RefCell;
7use std::ops::Range;
8use std::ptr::NonNull;
9use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
10use std::sync::{Arc, RwLock};
11use std::time::{Duration, Instant};
12use wasmtime_environ::Trap;
13
14#[derive(Clone)]
23pub struct SharedMemory(Arc<SharedMemoryInner>);
24
25struct SharedMemoryInner {
26 memory: RwLock<LocalMemory>,
27 spot: ParkingSpot,
28 ty: wasmtime_environ::Memory,
29 def: LongTermVMMemoryDefinition,
30}
31
32impl SharedMemory {
33 pub fn new(engine: &Engine, ty: &wasmtime_environ::Memory) -> Result<Self> {
35 let tunables = engine.tunables();
36 let memory_tunables = wasmtime_environ::MemoryTunables::new(
37 tunables,
38 wasmtime_environ::MemoryKind::LinearMemory,
39 );
40 let (minimum_bytes, maximum_bytes) = vm::assert_ready(Memory::limit_new(ty, None))?;
43 let mmap_memory = MmapMemory::new(ty, &memory_tunables, minimum_bytes, maximum_bytes)?;
44 let boxed: Box<dyn crate::runtime::vm::RuntimeLinearMemory> =
45 try_new::<Box<_>>(mmap_memory)?;
46 Self::wrap(
47 engine,
48 ty,
49 LocalMemory::new(ty, &memory_tunables, boxed, None)?,
50 )
51 }
52
53 pub fn wrap(
55 engine: &Engine,
56 ty: &wasmtime_environ::Memory,
57 memory: LocalMemory,
58 ) -> Result<Self> {
59 if !engine.config().shared_memory {
60 bail!(
61 "shared memory support is disabled for this engine -- see `Config::shared_memory`"
62 );
63 }
64 if !ty.shared {
65 bail!("shared memory must have a `shared` memory type");
66 }
67 Ok(Self(try_new::<Arc<_>>(SharedMemoryInner {
68 ty: *ty,
69 spot: ParkingSpot::default(),
70 def: LongTermVMMemoryDefinition(memory.vmmemory()),
71 memory: RwLock::new(memory),
72 })?))
73 }
74
75 pub fn ty(&self) -> &wasmtime_environ::Memory {
77 &self.0.ty
78 }
79
80 pub fn as_memory(self) -> Memory {
82 Memory::Shared(self)
83 }
84
85 pub fn vmmemory_ptr(&self) -> NonNull<VMMemoryDefinition> {
87 NonNull::from(&self.0.def.0)
88 }
89
90 pub fn grow(&self, delta_pages: u64) -> Result<Option<(usize, usize)>, Error> {
92 let mut memory = self.0.memory.write().unwrap();
93 let result = vm::assert_ready(memory.grow(delta_pages, None))?;
96 if let Some((_old_size_in_bytes, new_size_in_bytes)) = result {
97 self.0
117 .def
118 .0
119 .current_length
120 .store(new_size_in_bytes, Ordering::SeqCst);
121 }
122 Ok(result)
123 }
124
125 pub fn atomic_notify(&self, addr_index: u64, count: u32) -> Result<u32, Trap> {
127 let ptr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
128 log::trace!("memory.atomic.notify(addr={addr_index:#x}, count={count})");
129 let ptr = unsafe { &*ptr };
130 Ok(self.0.spot.notify(ptr, count))
131 }
132
133 pub fn atomic_wait32(
135 &self,
136 addr_index: u64,
137 expected: u32,
138 timeout: Option<Duration>,
139 ) -> Result<WaitResult, Trap> {
140 let addr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
141 log::trace!(
142 "memory.atomic.wait32(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
143 );
144
145 assert!(std::mem::size_of::<AtomicU32>() == 4);
147 assert!(std::mem::align_of::<AtomicU32>() <= 4);
148 let atomic = unsafe { AtomicU32::from_ptr(addr.cast()) };
149
150 let deadline = timeout.and_then(|d| Instant::now().checked_add(d));
155
156 WAITER.with(|waiter| {
157 let mut waiter = waiter.borrow_mut();
158 Ok(self.0.spot.wait32(atomic, expected, deadline, &mut waiter))
159 })
160 }
161
162 pub fn atomic_wait64(
164 &self,
165 addr_index: u64,
166 expected: u64,
167 timeout: Option<Duration>,
168 ) -> Result<WaitResult, Trap> {
169 let addr = validate_atomic_addr(&self.0.def.0, addr_index, 8, 8)?;
170 log::trace!(
171 "memory.atomic.wait64(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
172 );
173
174 assert!(std::mem::size_of::<AtomicU64>() == 8);
176 assert!(std::mem::align_of::<AtomicU64>() <= 8);
177 let atomic = unsafe { AtomicU64::from_ptr(addr.cast()) };
178
179 let deadline = timeout.and_then(|d| Instant::now().checked_add(d));
181
182 WAITER.with(|waiter| {
183 let mut waiter = waiter.borrow_mut();
184 Ok(self.0.spot.wait64(atomic, expected, deadline, &mut waiter))
185 })
186 }
187
188 pub(crate) fn byte_size(&self) -> usize {
189 self.0.memory.read().unwrap().byte_size()
190 }
191
192 pub(crate) fn needs_init(&self) -> bool {
193 self.0.memory.read().unwrap().needs_init()
194 }
195
196 pub(crate) fn wasm_accessible(&self) -> Range<usize> {
197 self.0.memory.read().unwrap().wasm_accessible()
198 }
199}
200
201thread_local! {
202 static WAITER: RefCell<Waiter> = const { RefCell::new(Waiter::new()) };
205}
206
207struct LongTermVMMemoryDefinition(VMMemoryDefinition);
216unsafe impl Send for LongTermVMMemoryDefinition {}
217unsafe impl Sync for LongTermVMMemoryDefinition {}