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