Skip to main content

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}