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