Skip to main content

euv_engine/cell/
impl.rs

1use super::*;
2
3// ===================================================================
4// Accessor impls (the `#[derive(Data)]` Lombok derive is intentionally
5// not applied to either struct - see `struct.rs` for the rationale,
6// chiefly that Lombok requires `T: Sized` while our cells expose the
7// `T: ?Sized` bound).
8//
9// These hand-written accessors mirror the contract Lombok's `Data`
10// derive would have produced:
11//
12//   `pub fn get_inner(&self) -> &UnsafeCell<T>`               (both)
13//   `pub fn set_inner(&self, val: UnsafeCell<Option<T>>) -> UnsafeCell<Option<T>>`
14//                                                            (MaybeEngineCell only)
15//
16// `EngineCell` deliberately does NOT expose `set_inner` because
17// `T: ?Sized` rules out `mem::replace` of the inner UnsafeCell; in
18// practice the public surface of `EngineCell` never needs to swap
19// the whole backing storage.
20//
21// All other impls in this file MUST go through these accessors
22// rather than touching `self.inner` directly.
23// ===================================================================
24
25/// Accessor implementations for [`EngineCell`].
26impl<T: ?Sized> EngineCell<T> {
27    /// Returns a shared reference to the backing `UnsafeCell<T>`.
28    ///
29    /// # Returns
30    ///
31    /// - `&UnsafeCell<T>` - The backing storage of the cell, borrowed
32    ///   for the lifetime of the cell.
33    pub fn get_inner(&self) -> &UnsafeCell<T> {
34        &self.inner
35    }
36}
37
38/// Accessor implementations for [`MaybeEngineCell`].
39impl<T> MaybeEngineCell<T> {
40    /// Returns a shared reference to the backing `UnsafeCell<Option<T>>`.
41    ///
42    /// # Returns
43    ///
44    /// - `UnsafeCell<Option<T>>` - A `UnsafeCell<Option<T>>` value.
45    pub fn get_inner(&self) -> &UnsafeCell<Option<T>> {
46        &self.inner
47    }
48
49    /// Replaces the backing storage with `val`, returning the previous
50    /// `UnsafeCell<Option<T>>`.
51    ///
52    /// # Arguments
53    ///
54    /// - `UnsafeCell<Option<T>>` - The new backing storage to
55    ///   install.
56    ///
57    /// # Returns
58    ///
59    /// - `UnsafeCell<Option<T>>` - The previous backing storage.
60    ///
61    /// Implemented via `core::mem::replace`. `MaybeEngineCell<T>`
62    /// carries the implicit `Sized` bound (it is `T: Sized` here
63    /// because `Option<T>` is `Sized` only when `T` is); `mem::replace`
64    /// therefore applies. The returned previous backing storage is
65    /// the caller's responsibility to drop.
66    ///
67    /// Note: `set_inner` is currently unused by the rest of the
68    /// module (the `try_*` paths go straight through raw pointer
69    /// reads/writes). It is kept here as the Lombok-shaped
70    /// counterpart for parity with `EngineCell::get_inner`, in case
71    /// future code wants to swap the whole backing storage.
72    pub fn set_inner(&mut self, val: UnsafeCell<Option<T>>) -> UnsafeCell<Option<T>> {
73        // `mem::replace` requires `T: Sized` - which holds for
74        // `MaybeEngineCell<T>` because `Option<T>` is only `Sized`
75        // when `T` is. The borrow of `self.inner` is the single
76        // mutable access point under the cell's single-threaded
77        // contract.
78        mem::replace(&mut self.inner, val)
79    }
80}
81
82// ===================================================================
83// `Sync` impls (blanket markers, run as the first impl block per §9).
84//
85// SAFETY: see `struct.rs` doc comments. The engine runs only on the
86// single wasm thread; concurrent access from multiple threads is
87// undefined. Aligns with the `Sync` newtype shape used by
88// `core::reactive::hook::impl::HookContext::current`.
89// ===================================================================
90
91/// Marker that `EngineCell<T>` is safe to share across the wasm main
92/// thread under single-threaded access.
93unsafe impl<T: ?Sized> Sync for EngineCell<T> {}
94
95/// Marker that `MaybeEngineCell<T>` is safe to share across the wasm
96/// main thread under single-threaded access.
97unsafe impl<T> Sync for MaybeEngineCell<T> {}
98
99// ===================================================================
100// `Default` impls (run before body impls per §9).
101// ===================================================================
102
103/// `Default` impl for [`EngineCell`].
104///
105/// Only available when `T: Default + Sized`. The `?Sized` bound on
106/// the cell prevents adding `Default` blanket-style because trait
107/// `Default` cannot be implemented for unsized types.
108impl<T: Default> Default for EngineCell<T> {
109    /// Creates a default cell by installing `T::default()` as the
110    /// initial value.
111    fn default() -> Self {
112        Self::new(T::default())
113    }
114}
115
116/// `Default` impl for [`MaybeEngineCell`].
117impl<T> Default for MaybeEngineCell<T> {
118    /// Constructs a default [`MaybeEngineCell`] value.
119    fn default() -> Self {
120        Self::new()
121    }
122}
123
124// ===================================================================
125// Body impls (constructor + accessors).
126//
127// All read sites go through the `get_inner` accessor and then
128// dereference via the standard `UnsafeCell::get()` raw-pointer
129// escape. Write sites that need to replace the whole backing
130// storage use `set_inner` (MaybeEngineCell) or none at all
131// (EngineCell, which is never required to swap its backing storage).
132// There are NO direct field accesses in this block - the
133// field-access rule in `struct.rs` is the single source of truth.
134// ===================================================================
135
136/// Constructor + read accessors for [`EngineCell`].
137impl<T: ?Sized> EngineCell<T> {
138    /// Creates a new cell with the given initial value.
139    ///
140    /// Construction is the one place where direct field initialisation
141    /// is permitted (see field-access rule in `struct.rs`); all other
142    /// sites must go through the accessors.
143    ///
144    /// # Arguments
145    ///
146    /// - `T: Sized` - A generic type parameter.
147    pub fn new(value: T) -> Self
148    where
149        T: Sized,
150    {
151        Self {
152            inner: UnsafeCell::new(value),
153        }
154    }
155
156    /// Returns a mutable reference to the contained value.
157    ///
158    /// # Safety
159    ///
160    /// The borrow MUST be exclusive - no other `get`, `get_mut`,
161    /// `try_get`, or `try_get_mut` on the same cell may be alive when
162    /// the returned reference is used. Two concurrent mutable borrows on
163    /// a wasm single-threaded runtime are well-defined in practice only
164    /// because wasm has no data-race detection; the `&'static mut`
165    /// return type tells the borrow checker you promise exclusivity.
166    ///
167    /// Reads via the `get_inner` accessor; the resulting
168    /// `&UnsafeCell<T>` is then turned into a raw pointer by
169    /// `UnsafeCell::get()` so we can hand the caller the
170    /// `&'static mut T` the rest of the engine expects.
171    ///
172    /// # Returns
173    ///
174    /// - `'static mut T` - A `'static mut T` value.
175    pub fn get_mut(&self) -> &'static mut T {
176        let inner: &UnsafeCell<T> = self.get_inner();
177        unsafe { &mut *inner.get() }
178    }
179
180    /// Returns a shared reference to the contained value.
181    ///
182    /// # Safety
183    ///
184    /// The lifetime of `&'static T` extends beyond what the borrow
185    /// checker can prove. Callers MUST NOT use this while a mutable
186    /// borrow on the same cell is alive. Use [`Self::get_mut`]
187    /// exclusively for write access.
188    ///
189    /// Reads via the `get_inner` accessor + `UnsafeCell::get`.
190    ///
191    /// # Returns
192    ///
193    /// - `'static T` - The current value (or a snapshot thereof).
194    pub fn get(&self) -> &'static T {
195        let inner: &UnsafeCell<T> = self.get_inner();
196        unsafe { &*inner.get() }
197    }
198}
199
200/// Constructor + accessors for [`MaybeEngineCell`].
201impl<T> MaybeEngineCell<T> {
202    /// Creates an empty cell.
203    ///
204    /// Struct literal is permitted by the field-access rule for the
205    /// constructor only.
206    pub const fn new() -> Self {
207        Self {
208            inner: UnsafeCell::new(None),
209        }
210    }
211
212    /// If the cell contains a value, returns a shared reference to it.
213    ///
214    /// # Returns
215    ///
216    /// - `Option<'static T>` - Optional reference to the inner value, or `None`.
217    pub fn try_get(&self) -> Option<&'static T> {
218        let inner: &UnsafeCell<Option<T>> = self.get_inner();
219        let slot: &Option<T> = unsafe { &*inner.get() };
220        slot.as_ref()
221    }
222
223    /// If the cell contains a value, returns a mutable reference to it.
224    ///
225    /// # Safety
226    ///
227    /// Exclusivity rules from [`EngineCell::get_mut`] apply - no other
228    /// borrow on the same cell may be alive.
229    ///
230    /// # Returns
231    ///
232    /// - `Option<'static mut T>` - Optional mutable reference to the inner value, or `None`.
233    pub fn try_get_mut(&self) -> Option<&'static mut T> {
234        let inner: &UnsafeCell<Option<T>> = self.get_inner();
235        let slot: &mut Option<T> = unsafe { &mut *inner.get() };
236        slot.as_mut()
237    }
238
239    /// Installs `value` into the cell. Returns `Err(value)` if the cell
240    /// is already populated; the caller may then retry or drop it.
241    ///
242    /// Reads through `get_inner` to inspect the current state without
243    /// aliasing, then writes through `UnsafeCell::get` raw pointer.
244    ///
245    /// # Arguments
246    ///
247    /// - `T` - Value to store.
248    ///
249    /// # Returns
250    ///
251    /// - `Result<(), T>` - `Ok(())` on success, or `Err(value)` if the cell was occupied.
252    pub fn try_set(&self, value: T) -> Result<(), T> {
253        let inner: &UnsafeCell<Option<T>> = self.get_inner();
254        let slot: *mut Option<T> = inner.get();
255        unsafe {
256            if (*slot).is_some() {
257                return Err(value);
258            }
259            *slot = Some(value);
260        }
261        Ok(())
262    }
263
264    /// Removes and returns the contained value, leaving the cell empty.
265    ///
266    /// # Returns
267    ///
268    /// - `Option<T>` - The taken value, or `None` if the cell was empty.
269    pub fn try_take(&self) -> Option<T> {
270        let inner: &UnsafeCell<Option<T>> = self.get_inner();
271        unsafe { (*inner.get()).take() }
272    }
273
274    /// Replaces the contained value, returning the old one.
275    ///
276    /// # Arguments
277    ///
278    /// - `T` - Replacement value.
279    ///
280    /// # Returns
281    ///
282    /// - `Option<T>` - The previously stored value, or `None`.
283    pub fn try_replace(&self, value: T) -> Option<T> {
284        let inner: &UnsafeCell<Option<T>> = self.get_inner();
285        let slot: *mut Option<T> = inner.get();
286        unsafe { (*slot).replace(value) }
287    }
288}