Skip to main content

rill_core/buffer/
storage.rs

1//! # Safe atomic cell
2//!
3//! Provides a safe wrapper around `UnsafeCell` with a fully safe API.
4//! The constructor guarantees correct initialisation; if creation is impossible
5//! it panics (since continued operation is not possible).
6
7use std::cell::UnsafeCell;
8use std::fmt;
9
10/// Atomic cell with a fully safe API.
11///
12/// Wraps `UnsafeCell` to provide interior mutability without requiring `T: Default`.
13///
14/// # Safety
15/// - The constructor guarantees correct initialisation.
16/// - All memory operations are encapsulated.
17/// - Panics if creation is impossible (system error).
18#[repr(transparent)]
19pub struct AtomicCell<T> {
20    inner: UnsafeCell<T>,
21}
22
23#[allow(unsafe_code)]
24impl<T> AtomicCell<T> {
25    /// Load the value (requires no concurrent writes).
26    #[inline]
27    pub fn load(&self) -> T
28    where
29        T: Copy,
30    {
31        // SAFETY:
32        // - The caller guarantees no concurrent write.
33        // - Relaxed ordering is sufficient.
34        // - The value is always correctly initialised.
35        unsafe { *self.inner.get() }
36    }
37
38    /// Store a new value (requires unique write access, no concurrent reads).
39    #[inline]
40    pub fn store(&self, value: T) {
41        // SAFETY:
42        // - Unique write access is guaranteed.
43        // - The value is correctly initialised.
44        // - Relaxed ordering is sufficient.
45        unsafe {
46            *self.inner.get() = value;
47        }
48    }
49
50    /// Get a raw pointer to the data (for compatibility).
51    #[inline]
52    pub fn as_ptr(&self) -> *mut T {
53        self.inner.get()
54    }
55
56    /// Create a new atomic cell.
57    #[inline]
58    pub const fn new(value: T) -> Self {
59        Self {
60            inner: UnsafeCell::new(value),
61        }
62    }
63
64    /// Create a new atomic cell with validation.
65    ///
66    /// # Errors
67    /// Returns `AtomicCellError::TypeTooLarge` if the type is too large.
68    pub fn try_new(value: T) -> Result<Self, AtomicCellError> {
69        if std::mem::size_of::<T>() > isize::MAX as usize {
70            return Err(AtomicCellError::TypeTooLarge);
71        }
72        Ok(Self::new(value))
73    }
74}
75
76impl<T: Clone + Copy> Clone for AtomicCell<T> {
77    fn clone(&self) -> Self {
78        Self::new(self.load())
79    }
80}
81
82impl<T: Copy + fmt::Debug> fmt::Debug for AtomicCell<T> {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        f.debug_struct("AtomicCell")
85            .field("value", &self.load())
86            .finish()
87    }
88}
89
90impl<T: Default> Default for AtomicCell<T> {
91    fn default() -> Self {
92        Self::new(T::default())
93    }
94}
95
96/// Errors that can occur when creating an [`AtomicCell`].
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum AtomicCellError {
99    /// The type is too large for an atomic cell.
100    TypeTooLarge,
101    /// Out of memory (extremely rare).
102    OutOfMemory,
103}
104
105impl fmt::Display for AtomicCellError {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        match self {
108            AtomicCellError::TypeTooLarge => write!(f, "Type too large for atomic cell"),
109            AtomicCellError::OutOfMemory => write!(f, "Out of memory"),
110        }
111    }
112}
113
114impl std::error::Error for AtomicCellError {}
115
116// AtomicCell can only be Send/Sync if T: Send/Sync
117#[allow(unsafe_code)]
118unsafe impl<T: Send> Send for AtomicCell<T> {}
119#[allow(unsafe_code)]
120unsafe impl<T: Sync> Sync for AtomicCell<T> {}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_atomic_cell_basic() {
128        let cell = AtomicCell::new(42);
129        assert_eq!(cell.load(), 42);
130
131        cell.store(100);
132        assert_eq!(cell.load(), 100);
133    }
134
135    #[test]
136    fn test_atomic_cell_try_new() {
137        let cell = AtomicCell::try_new(42).unwrap();
138        assert_eq!(cell.load(), 42);
139    }
140
141    #[test]
142    fn test_atomic_cell_default() {
143        let cell = AtomicCell::<i32>::default();
144        assert_eq!(cell.load(), 0);
145    }
146
147    #[test]
148    fn test_atomic_cell_clone() {
149        let cell1 = AtomicCell::new(42);
150        let cell2 = cell1.clone();
151        assert_eq!(cell2.load(), 42);
152    }
153}