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}