zencan_common/
atomic_cell.rs

1//! Implements an AtomicCell type which uses critical_section Mutex to enforce atomic store/load
2//!
3//! Since Crossbeam does not support thumbv6m due to lack of CAS, this is a fallback option.
4//!
5//! TODO: This can be made more efficient by using core::sync::atomic types for types which can
6//! transmute into the base integer types.
7
8use core::{cell::Cell, ops::Add};
9use critical_section::Mutex;
10
11use crate::traits::LoadStore;
12
13/// A container to allow atomic access to the contained object
14pub struct AtomicCell<T> {
15    inner: Mutex<Cell<T>>,
16}
17
18impl<T: Send + Copy> AtomicCell<T> {
19    /// Read the value of the AtomicCell
20    pub fn load(&self) -> T {
21        critical_section::with(|cs| self.inner.borrow(cs).get())
22    }
23
24    /// Borrow a reference to the contained value
25    ///
26    /// A critical section must be obtained by the called and provided
27    pub fn borrow<'a>(&'a self, cs: critical_section::CriticalSection<'a>) -> &'a Cell<T> {
28        self.inner.borrow(cs)
29    }
30
31    /// Perform atomic modification of the contained value
32    ///
33    /// This operation will be performed in a critical section, so it will block all IRQs until the
34    /// function returns.
35    pub fn fetch_update(&self, mut f: impl FnMut(T) -> Option<T>) -> Result<T, T> {
36        critical_section::with(|cs| {
37            let old_value = self.inner.borrow(cs).get();
38            if let Some(new_value) = f(old_value) {
39                self.inner.borrow(cs).set(new_value);
40                Ok(old_value)
41            } else {
42                Err(old_value)
43            }
44        })
45    }
46}
47
48impl<T: Send> AtomicCell<T> {
49    /// Create a new AtomicCell with the provided value
50    pub const fn new(value: T) -> Self {
51        Self {
52            inner: Mutex::new(Cell::new(value)),
53        }
54    }
55
56    /// Replace the value of the AtomicCell
57    pub fn store(&self, value: T) {
58        critical_section::with(|cs| self.inner.borrow(cs).set(value));
59    }
60}
61
62impl<T: Send + Default> AtomicCell<T> {
63    /// Return the contained value, and replace it with a default value
64    pub fn take(&self) -> T {
65        critical_section::with(|cs| self.inner.borrow(cs).take())
66    }
67}
68
69impl<T: Copy + Add<Output = T>> AtomicCell<T> {
70    /// Atomically add value to the contained value
71    pub fn fetch_add(&self, value: T) -> T {
72        critical_section::with(|cs| {
73            let old_value = self.inner.borrow(cs).get();
74            self.inner.borrow(cs).set(old_value + value);
75            old_value
76        })
77    }
78}
79
80impl<T: Default> Default for AtomicCell<T> {
81    fn default() -> Self {
82        Self {
83            inner: Mutex::new(Cell::new(T::default())),
84        }
85    }
86}
87
88impl<T: Default + Copy + Send> LoadStore<T> for AtomicCell<T> {
89    fn load(&self) -> T {
90        self.load()
91    }
92
93    fn store(&self, value: T) {
94        self.store(value)
95    }
96}