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}