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}