tudelft_quadrupel/mutex.rs
1use core::cell::UnsafeCell;
2
3/// A mutual exclusion primitive useful for protecting shared data. It works by disabling interrupts while the lock is being held.
4///
5/// This is implementation is only sound on the NRF51822 or other single-core processors.
6pub struct Mutex<T> {
7    inner: UnsafeCell<T>,
8}
9
10// SAFETY: it is safe to share a Mutex between interrupts and
11// other code. That's because there is only one thread, and the
12// only way to access what is in a Mutex is by locking it, disabling
13// interrupts. That means there are two cases:
14
15// 1. We are in normal code, there can be no interrupt (since we turned those off)
16// and since there's only one core, we know for sure we're alone in accessing the
17// wrapped value
18//
19// 2. We are in an interrupt. In an interrupt, no other interrupts can occur. It
20// is also impossible to have already locked the Mutex at this point, since to lock
21// it outside an interrupt, interrupts had to be turned off. That means when we are
22// in an interrupt, nothing else can have the Mutex locked, otherwise we could not
23// actually be in an interrupt.
24unsafe impl<T> Sync for Mutex<T> {}
25
26impl<T> Mutex<T> {
27    /// Create a new Mutex.
28    pub const fn new(v: T) -> Self {
29        Self {
30            inner: UnsafeCell::new(v),
31        }
32    }
33
34    /// Locks the mutex in a callback
35    #[inline(always)]
36    pub fn modify<U>(&self, f: impl FnOnce(&mut T) -> U) -> U {
37        cortex_m::interrupt::free(|_| {
38            // Safety: Interrupts are disabled, so the current code is the only one that can be running.
39            // TODO make sure you can't lock the mutex in here
40            f(unsafe { &mut *self.inner.get() })
41        })
42    }
43
44    /// This function gets a reference to the inner `T` _without_ locking the lock.
45    /// This is inherently unsafe.
46    ///
47    /// # Safety
48    /// This function is only safe if you can guarantee that no mutable references to the contents of this lock exist.
49    ///
50    /// This generally can be used in the following cases:
51    /// * You only access this mutex from within a single interrupt
52    /// * You only access this mutex outside of interrupts, as interrupts don't break the mutex guarantees
53    pub unsafe fn no_critical_section_lock(&self) -> &T {
54        &*self.inner.get()
55    }
56
57    /// This function gets a mutable reference to the inner `T` _without_ locking the lock.
58    /// This is inherently unsafe.
59    ///
60    /// # Safety
61    /// This function is only safe if you can guarantee that no other references to the contents of this lock exist.
62    ///
63    /// This generally can be used in the following cases:
64    /// * You only access this mutex from within a single interrupt
65    /// * You only access this mutex outside of interrupts, as interrupts don't break the mutex guarantees
66    #[allow(clippy::mut_from_ref)]
67    pub unsafe fn no_critical_section_lock_mut(&self) -> &mut T {
68        &mut *self.inner.get()
69    }
70}