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}