10a55f804SAlex Crichton use crate::Engine;
2d3132c9dSAlex Crichton use crate::prelude::*;
390ac295eSAlex Crichton use crate::runtime::vm::memory::{LocalMemory, MmapMemory, validate_atomic_addr};
4d3132c9dSAlex Crichton use crate::runtime::vm::parking_spot::{ParkingSpot, Waiter};
5f881ab24SAlex Crichton use crate::runtime::vm::{self, Memory, VMMemoryDefinition, WaitResult};
6d3132c9dSAlex Crichton use std::cell::RefCell;
7d3132c9dSAlex Crichton use std::ops::Range;
8b86b9682SAlex Crichton use std::ptr::NonNull;
9d3132c9dSAlex Crichton use std::sync::atomic::{AtomicU32, AtomicU64, Ordering};
10d3132c9dSAlex Crichton use std::sync::{Arc, RwLock};
11d3132c9dSAlex Crichton use std::time::{Duration, Instant};
120a55f804SAlex Crichton use wasmtime_environ::Trap;
13d3132c9dSAlex Crichton 
14d3132c9dSAlex Crichton /// For shared memory (and only for shared memory), this lock-version restricts
15d3132c9dSAlex Crichton /// access when growing the memory or checking its size. This is to conform with
16d3132c9dSAlex Crichton /// the [thread proposal]: "When `IsSharedArrayBuffer(...)` is true, the return
17d3132c9dSAlex Crichton /// value should be the result of an atomic read-modify-write of the new size to
18d3132c9dSAlex Crichton /// the internal `length` slot."
19d3132c9dSAlex Crichton ///
20d3132c9dSAlex Crichton /// [thread proposal]:
21d3132c9dSAlex Crichton ///     https://github.com/WebAssembly/threads/blob/master/proposals/threads/Overview.md#webassemblymemoryprototypegrow
22d3132c9dSAlex Crichton #[derive(Clone)]
23d3132c9dSAlex Crichton pub struct SharedMemory(Arc<SharedMemoryInner>);
24d3132c9dSAlex Crichton 
25d3132c9dSAlex Crichton struct SharedMemoryInner {
26d3132c9dSAlex Crichton     memory: RwLock<LocalMemory>,
27d3132c9dSAlex Crichton     spot: ParkingSpot,
28d3132c9dSAlex Crichton     ty: wasmtime_environ::Memory,
29d3132c9dSAlex Crichton     def: LongTermVMMemoryDefinition,
30d3132c9dSAlex Crichton }
31d3132c9dSAlex Crichton 
32d3132c9dSAlex Crichton impl SharedMemory {
33d3132c9dSAlex Crichton     /// Construct a new [`SharedMemory`].
new(engine: &Engine, ty: &wasmtime_environ::Memory) -> Result<Self>340a55f804SAlex Crichton     pub fn new(engine: &Engine, ty: &wasmtime_environ::Memory) -> Result<Self> {
350a55f804SAlex Crichton         let tunables = engine.tunables();
36e1f50aadSAlex Crichton         // Note that without a limiter being passed to `limit_new` this
37e1f50aadSAlex Crichton         // `assert_ready` should never panic.
38e1f50aadSAlex Crichton         let (minimum_bytes, maximum_bytes) = vm::assert_ready(Memory::limit_new(ty, None))?;
39d3132c9dSAlex Crichton         let mmap_memory = MmapMemory::new(ty, tunables, minimum_bytes, maximum_bytes)?;
40*439de7fbSNick Fitzgerald         let boxed: Box<dyn crate::runtime::vm::RuntimeLinearMemory> =
41*439de7fbSNick Fitzgerald             try_new::<Box<_>>(mmap_memory)?;
42*439de7fbSNick Fitzgerald         Self::wrap(engine, ty, LocalMemory::new(ty, tunables, boxed, None)?)
43d3132c9dSAlex Crichton     }
44d3132c9dSAlex Crichton 
45d3132c9dSAlex Crichton     /// Wrap an existing [Memory] with the locking provided by a [SharedMemory].
wrap( engine: &Engine, ty: &wasmtime_environ::Memory, memory: LocalMemory, ) -> Result<Self>460a55f804SAlex Crichton     pub fn wrap(
470a55f804SAlex Crichton         engine: &Engine,
480a55f804SAlex Crichton         ty: &wasmtime_environ::Memory,
490a55f804SAlex Crichton         memory: LocalMemory,
500a55f804SAlex Crichton     ) -> Result<Self> {
510a55f804SAlex Crichton         if !engine.config().shared_memory {
520a55f804SAlex Crichton             bail!(
530a55f804SAlex Crichton                 "shared memory support is disabled for this engine -- see `Config::shared_memory`"
540a55f804SAlex Crichton             );
550a55f804SAlex Crichton         }
56d3132c9dSAlex Crichton         if !ty.shared {
57d3132c9dSAlex Crichton             bail!("shared memory must have a `shared` memory type");
58d3132c9dSAlex Crichton         }
59*439de7fbSNick Fitzgerald         Ok(Self(try_new::<Arc<_>>(SharedMemoryInner {
60d3132c9dSAlex Crichton             ty: *ty,
61d3132c9dSAlex Crichton             spot: ParkingSpot::default(),
62d3132c9dSAlex Crichton             def: LongTermVMMemoryDefinition(memory.vmmemory()),
63d3132c9dSAlex Crichton             memory: RwLock::new(memory),
64*439de7fbSNick Fitzgerald         })?))
65d3132c9dSAlex Crichton     }
66d3132c9dSAlex Crichton 
67d3132c9dSAlex Crichton     /// Return the memory type for this [`SharedMemory`].
ty(&self) -> &wasmtime_environ::Memory68b860c2c6SAlex Crichton     pub fn ty(&self) -> &wasmtime_environ::Memory {
69b860c2c6SAlex Crichton         &self.0.ty
70d3132c9dSAlex Crichton     }
71d3132c9dSAlex Crichton 
72d3132c9dSAlex Crichton     /// Convert this shared memory into a [`Memory`].
as_memory(self) -> Memory73d3132c9dSAlex Crichton     pub fn as_memory(self) -> Memory {
74d3132c9dSAlex Crichton         Memory::Shared(self)
75d3132c9dSAlex Crichton     }
76d3132c9dSAlex Crichton 
77d3132c9dSAlex Crichton     /// Return a pointer to the shared memory's [VMMemoryDefinition].
vmmemory_ptr(&self) -> NonNull<VMMemoryDefinition>78b86b9682SAlex Crichton     pub fn vmmemory_ptr(&self) -> NonNull<VMMemoryDefinition> {
79b86b9682SAlex Crichton         NonNull::from(&self.0.def.0)
80d3132c9dSAlex Crichton     }
81d3132c9dSAlex Crichton 
82d3132c9dSAlex Crichton     /// Same as `RuntimeLinearMemory::grow`, except with `&self`.
grow(&self, delta_pages: u64) -> Result<Option<(usize, usize)>, Error>83f881ab24SAlex Crichton     pub fn grow(&self, delta_pages: u64) -> Result<Option<(usize, usize)>, Error> {
84d3132c9dSAlex Crichton         let mut memory = self.0.memory.write().unwrap();
85f881ab24SAlex Crichton         // Without a limiter being passed in this shouldn't have an await point,
86f881ab24SAlex Crichton         // so it should be safe to assert that it's ready.
87f881ab24SAlex Crichton         let result = vm::assert_ready(memory.grow(delta_pages, None))?;
88d3132c9dSAlex Crichton         if let Some((_old_size_in_bytes, new_size_in_bytes)) = result {
89d3132c9dSAlex Crichton             // Store the new size to the `VMMemoryDefinition` for JIT-generated
90d3132c9dSAlex Crichton             // code (and runtime functions) to access. No other code can be
91d3132c9dSAlex Crichton             // growing this memory due to the write lock, but code in other
92d3132c9dSAlex Crichton             // threads could have access to this shared memory and we want them
93d3132c9dSAlex Crichton             // to see the most consistent version of the `current_length`; a
94d3132c9dSAlex Crichton             // weaker consistency is possible if we accept them seeing an older,
95d3132c9dSAlex Crichton             // smaller memory size (assumption: memory only grows) but presently
96d3132c9dSAlex Crichton             // we are aiming for accuracy.
97d3132c9dSAlex Crichton             //
98d3132c9dSAlex Crichton             // Note that it could be possible to access a memory address that is
99d3132c9dSAlex Crichton             // now-valid due to changes to the page flags in `grow` above but
100d3132c9dSAlex Crichton             // beyond the `memory.size` that we are about to assign to. In these
101d3132c9dSAlex Crichton             // and similar cases, discussion in the thread proposal concluded
102d3132c9dSAlex Crichton             // that: "multiple accesses in one thread racing with another
103d3132c9dSAlex Crichton             // thread's `memory.grow` that are in-bounds only after the grow
104d3132c9dSAlex Crichton             // commits may independently succeed or trap" (see
105d3132c9dSAlex Crichton             // https://github.com/WebAssembly/threads/issues/26#issuecomment-433930711).
106d3132c9dSAlex Crichton             // In other words, some non-determinism is acceptable when using
107d3132c9dSAlex Crichton             // `memory.size` on work being done by `memory.grow`.
108d3132c9dSAlex Crichton             self.0
109d3132c9dSAlex Crichton                 .def
110d3132c9dSAlex Crichton                 .0
111d3132c9dSAlex Crichton                 .current_length
112d3132c9dSAlex Crichton                 .store(new_size_in_bytes, Ordering::SeqCst);
113d3132c9dSAlex Crichton         }
114d3132c9dSAlex Crichton         Ok(result)
115d3132c9dSAlex Crichton     }
116d3132c9dSAlex Crichton 
117d3132c9dSAlex Crichton     /// Implementation of `memory.atomic.notify` for this shared memory.
atomic_notify(&self, addr_index: u64, count: u32) -> Result<u32, Trap>118d3132c9dSAlex Crichton     pub fn atomic_notify(&self, addr_index: u64, count: u32) -> Result<u32, Trap> {
119d3132c9dSAlex Crichton         let ptr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
120d3132c9dSAlex Crichton         log::trace!("memory.atomic.notify(addr={addr_index:#x}, count={count})");
121d3132c9dSAlex Crichton         let ptr = unsafe { &*ptr };
122d3132c9dSAlex Crichton         Ok(self.0.spot.notify(ptr, count))
123d3132c9dSAlex Crichton     }
124d3132c9dSAlex Crichton 
125d3132c9dSAlex Crichton     /// Implementation of `memory.atomic.wait32` for this shared memory.
atomic_wait32( &self, addr_index: u64, expected: u32, timeout: Option<Duration>, ) -> Result<WaitResult, Trap>126d3132c9dSAlex Crichton     pub fn atomic_wait32(
127d3132c9dSAlex Crichton         &self,
128d3132c9dSAlex Crichton         addr_index: u64,
129d3132c9dSAlex Crichton         expected: u32,
130d3132c9dSAlex Crichton         timeout: Option<Duration>,
131d3132c9dSAlex Crichton     ) -> Result<WaitResult, Trap> {
132d3132c9dSAlex Crichton         let addr = validate_atomic_addr(&self.0.def.0, addr_index, 4, 4)?;
133d3132c9dSAlex Crichton         log::trace!(
134d3132c9dSAlex Crichton             "memory.atomic.wait32(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
135d3132c9dSAlex Crichton         );
136d3132c9dSAlex Crichton 
137d3132c9dSAlex Crichton         // SAFETY: `addr_index` was validated by `validate_atomic_addr` above.
138d3132c9dSAlex Crichton         assert!(std::mem::size_of::<AtomicU32>() == 4);
139d3132c9dSAlex Crichton         assert!(std::mem::align_of::<AtomicU32>() <= 4);
140d3132c9dSAlex Crichton         let atomic = unsafe { AtomicU32::from_ptr(addr.cast()) };
141d3132c9dSAlex Crichton         let deadline = timeout.map(|d| Instant::now() + d);
142d3132c9dSAlex Crichton 
143d3132c9dSAlex Crichton         WAITER.with(|waiter| {
144d3132c9dSAlex Crichton             let mut waiter = waiter.borrow_mut();
145d3132c9dSAlex Crichton             Ok(self.0.spot.wait32(atomic, expected, deadline, &mut waiter))
146d3132c9dSAlex Crichton         })
147d3132c9dSAlex Crichton     }
148d3132c9dSAlex Crichton 
149d3132c9dSAlex Crichton     /// Implementation of `memory.atomic.wait64` for this shared memory.
atomic_wait64( &self, addr_index: u64, expected: u64, timeout: Option<Duration>, ) -> Result<WaitResult, Trap>150d3132c9dSAlex Crichton     pub fn atomic_wait64(
151d3132c9dSAlex Crichton         &self,
152d3132c9dSAlex Crichton         addr_index: u64,
153d3132c9dSAlex Crichton         expected: u64,
154d3132c9dSAlex Crichton         timeout: Option<Duration>,
155d3132c9dSAlex Crichton     ) -> Result<WaitResult, Trap> {
156d3132c9dSAlex Crichton         let addr = validate_atomic_addr(&self.0.def.0, addr_index, 8, 8)?;
157d3132c9dSAlex Crichton         log::trace!(
158d3132c9dSAlex Crichton             "memory.atomic.wait64(addr={addr_index:#x}, expected={expected}, timeout={timeout:?})"
159d3132c9dSAlex Crichton         );
160d3132c9dSAlex Crichton 
161d3132c9dSAlex Crichton         // SAFETY: `addr_index` was validated by `validate_atomic_addr` above.
162d3132c9dSAlex Crichton         assert!(std::mem::size_of::<AtomicU64>() == 8);
163d3132c9dSAlex Crichton         assert!(std::mem::align_of::<AtomicU64>() <= 8);
164d3132c9dSAlex Crichton         let atomic = unsafe { AtomicU64::from_ptr(addr.cast()) };
165d3132c9dSAlex Crichton         let deadline = timeout.map(|d| Instant::now() + d);
166d3132c9dSAlex Crichton 
167d3132c9dSAlex Crichton         WAITER.with(|waiter| {
168d3132c9dSAlex Crichton             let mut waiter = waiter.borrow_mut();
169d3132c9dSAlex Crichton             Ok(self.0.spot.wait64(atomic, expected, deadline, &mut waiter))
170d3132c9dSAlex Crichton         })
171d3132c9dSAlex Crichton     }
172d3132c9dSAlex Crichton 
byte_size(&self) -> usize173d3132c9dSAlex Crichton     pub(crate) fn byte_size(&self) -> usize {
174d3132c9dSAlex Crichton         self.0.memory.read().unwrap().byte_size()
175d3132c9dSAlex Crichton     }
176d3132c9dSAlex Crichton 
needs_init(&self) -> bool177d3132c9dSAlex Crichton     pub(crate) fn needs_init(&self) -> bool {
178d3132c9dSAlex Crichton         self.0.memory.read().unwrap().needs_init()
179d3132c9dSAlex Crichton     }
180d3132c9dSAlex Crichton 
wasm_accessible(&self) -> Range<usize>181d3132c9dSAlex Crichton     pub(crate) fn wasm_accessible(&self) -> Range<usize> {
182d3132c9dSAlex Crichton         self.0.memory.read().unwrap().wasm_accessible()
183d3132c9dSAlex Crichton     }
184d3132c9dSAlex Crichton }
185d3132c9dSAlex Crichton 
186d3132c9dSAlex Crichton thread_local! {
187d3132c9dSAlex Crichton     /// Structure used in conjunction with `ParkingSpot` to block the current
188d3132c9dSAlex Crichton     /// thread if necessary. Note that this is lazily initialized.
189d3132c9dSAlex Crichton     static WAITER: RefCell<Waiter> = const { RefCell::new(Waiter::new()) };
190d3132c9dSAlex Crichton }
191d3132c9dSAlex Crichton 
192d3132c9dSAlex Crichton /// Shared memory needs some representation of a `VMMemoryDefinition` for
193d3132c9dSAlex Crichton /// JIT-generated code to access. This structure owns the base pointer and
194d3132c9dSAlex Crichton /// length to the actual memory and we share this definition across threads by:
195d3132c9dSAlex Crichton /// - never changing the base pointer; according to the specification, shared
196d3132c9dSAlex Crichton ///   memory must be created with a known maximum size so it can be allocated
197d3132c9dSAlex Crichton ///   once and never moved
198d3132c9dSAlex Crichton /// - carefully changing the length, using atomic accesses in both the runtime
199d3132c9dSAlex Crichton ///   and JIT-generated code.
200d3132c9dSAlex Crichton struct LongTermVMMemoryDefinition(VMMemoryDefinition);
201d3132c9dSAlex Crichton unsafe impl Send for LongTermVMMemoryDefinition {}
202d3132c9dSAlex Crichton unsafe impl Sync for LongTermVMMemoryDefinition {}
203