ove/cell.rs
1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Single-threaded interior-mutability primitives.
8//!
9//! [`LvCell<T>`] and [`LvRefCell<T>`] are thin newtypes around
10//! `core::cell::Cell` / `RefCell` that are `Sync`. The name carries a
11//! *contract*, not a runtime enforcement: the cell may only be mutated
12//! from one thread at a time. In LVGL apps the global LVGL lock enforces
13//! this; in other contexts, the caller must ensure single-threaded
14//! access (e.g. the cell is only touched inside a benchmark's
15//! setup/run/teardown on one thread at a time).
16
17/// `Cell<T>` for state mutated only from a single logical thread at a time.
18/// Marked `Sync` — the caller is responsible for upholding the
19/// single-access invariant (e.g. via a lock like LVGL's, or by structural
20/// guarantees in an embedded setup/run/teardown flow).
21///
22/// Pairs naturally with [`crate::InitCell`] for app-wide state.
23#[repr(transparent)]
24pub struct LvCell<T>(core::cell::Cell<T>);
25
26// SAFETY: The caller promises single-threaded access at any given time.
27// `T: Send` is required because observations may cross threads as long
28// as they don't overlap in time.
29unsafe impl<T: Send> Sync for LvCell<T> {}
30
31impl<T> LvCell<T> {
32 /// Construct a new cell holding `v`. Usable in `static`/`const` contexts.
33 pub const fn new(v: T) -> Self {
34 Self(core::cell::Cell::new(v))
35 }
36
37 /// Replace the value, returning the previous one.
38 pub fn replace(&self, v: T) -> T {
39 self.0.replace(v)
40 }
41
42 /// Store `v`.
43 pub fn set(&self, v: T) {
44 self.0.set(v);
45 }
46
47 /// Borrow the contents without copying.
48 ///
49 /// # Safety contract
50 ///
51 /// Same single-threaded-access invariant as the rest of `LvCell`:
52 /// the caller must ensure no concurrent mutation while the
53 /// returned reference is live. Useful for hot loops over large
54 /// `Copy` payloads (e.g. multi-byte buffers in benchmark inner
55 /// loops) where `get()`'s implicit copy would dominate.
56 #[inline]
57 pub fn get_ref(&self) -> &T {
58 // SAFETY: `LvCell` already promises single-threaded access at
59 // a time; readers and writers do not overlap. Cell::as_ptr
60 // returns the same address for the entire life of the cell,
61 // so the returned reference is valid for the cell's lifetime
62 // up to the next mutation.
63 unsafe { &*self.0.as_ptr() }
64 }
65
66 /// Raw pointer to the contents — same caveat as
67 /// [`core::cell::Cell::as_ptr`]. Combined with the LvCell
68 /// single-threaded-access invariant this lets a hot path build
69 /// a temporary `&mut T` for in-place updates without going
70 /// through `get()`'s Copy + `set()`'s Copy round-trip.
71 #[inline]
72 pub fn as_ptr(&self) -> *mut T {
73 self.0.as_ptr()
74 }
75}
76
77impl<T: Copy> LvCell<T> {
78 /// Read the current value (copies).
79 pub fn get(&self) -> T {
80 self.0.get()
81 }
82
83 /// Apply `f` to the current value and store the result.
84 pub fn update(&self, f: impl FnOnce(T) -> T) {
85 let v = self.0.get();
86 self.0.set(f(v));
87 }
88}
89
90/// `RefCell<T>` variant of [`LvCell`] for non-`Copy` state.
91#[repr(transparent)]
92pub struct LvRefCell<T>(core::cell::RefCell<T>);
93
94// SAFETY: `LvRefCell<T>` is a `RefCell`-shaped interior-mutable cell whose
95// borrow tracking is sound only while the LVGL lock is held. `T: Send` is
96// sufficient because all access happens inside the LVGL task or behind
97// `LvglGuard`, both of which serialise readers and the single writer.
98unsafe impl<T: Send> Sync for LvRefCell<T> {}
99
100impl<T> LvRefCell<T> {
101 /// Construct a new ref-cell. Usable in `static`/`const` contexts.
102 pub const fn new(v: T) -> Self {
103 Self(core::cell::RefCell::new(v))
104 }
105
106 /// Borrow the contents immutably (panics on outstanding mutable borrow).
107 pub fn borrow(&self) -> core::cell::Ref<'_, T> {
108 self.0.borrow()
109 }
110
111 /// Borrow the contents mutably (panics on outstanding borrow).
112 pub fn borrow_mut(&self) -> core::cell::RefMut<'_, T> {
113 self.0.borrow_mut()
114 }
115
116 /// Apply `f` to a mutable borrow.
117 pub fn with_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> R {
118 f(&mut *self.0.borrow_mut())
119 }
120}