stm32f1_hal/common/
atomic_mutex.rs

1use core::{
2    cell::UnsafeCell,
3    fmt::{self, Debug, Formatter},
4    ops::{Deref, DerefMut},
5    sync::atomic::*,
6};
7
8/// A simple atomic mutex for `no_std` environment.
9/// It can be used in interrupt context.
10/// But Most of the time, [`critical-section::Mutex`] is a better choice.
11pub struct AtomicMutex<T> {
12    data: UnsafeCell<T>,
13    state: AtomicBool,
14}
15
16unsafe impl<T> Send for AtomicMutex<T> {}
17unsafe impl<T> Sync for AtomicMutex<T> {}
18
19impl<T> Debug for AtomicMutex<T> {
20    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
21        f.write_str(match self.state.load(Ordering::Relaxed) {
22            true => "Locked",
23            _ => "Unlocked",
24        })
25    }
26}
27
28impl<T> AtomicMutex<T> {
29    /// Create a new populated instance.
30    pub const fn new(value: T) -> Self {
31        Self {
32            data: UnsafeCell::new(value),
33            state: AtomicBool::new(false),
34        }
35    }
36
37    /// Non-blocking, can be used in interrupt context
38    pub fn try_lock(&self) -> Option<AtomicMutexGuard<'_, T>> {
39        if self
40            .state
41            .compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed)
42            .is_ok()
43        {
44            Some(AtomicMutexGuard { m: self })
45        } else {
46            None
47        }
48    }
49
50    #[allow(clippy::mut_from_ref)]
51    #[inline]
52    fn get_data_mut(&self) -> &mut T {
53        unsafe { &mut *self.data.get() }
54    }
55}
56
57// ------------------------------------------------------------------------------------------------
58
59/// Holds the mutex until we are dropped
60#[derive(Debug)]
61pub struct AtomicMutexGuard<'mutex, T> {
62    m: &'mutex AtomicMutex<T>,
63}
64
65impl<'mutex, T> Deref for AtomicMutexGuard<'mutex, T> {
66    type Target = T;
67
68    fn deref(&self) -> &T {
69        self.m.get_data_mut()
70    }
71}
72
73impl<'mutex, T> DerefMut for AtomicMutexGuard<'mutex, T> {
74    fn deref_mut(&mut self) -> &mut T {
75        self.m.get_data_mut()
76    }
77}
78
79impl<'mutex, T> Drop for AtomicMutexGuard<'mutex, T> {
80    fn drop(&mut self) {
81        self.m.state.store(false, Ordering::Release);
82    }
83}
84
85// ------------------------------------------------------------------------------------------------
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[derive(Debug, PartialEq)]
92    struct TestData {
93        a: u8,
94        b: u8,
95    }
96
97    #[test]
98    fn lock() {
99        let l = AtomicMutex::<TestData>::new(TestData { a: 1, b: 2 });
100        {
101            let mut d = l.try_lock().unwrap();
102            assert_eq!(d.a, 1);
103            assert_eq!(d.b, 2);
104            let d2 = l.try_lock();
105            assert!(d2.is_none());
106            d.a += 1;
107        }
108    }
109}