riot_wrappers/
mutex.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! Data-carrying mutex built using RIOT's [mutex] module
//!
//! This roughly mimics [std::sync::Mutex].
//!
//! [mutex]: https://doc.riot-os.org/group__core__sync__mutex.html
//! [std::sync::mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html

use core::ops::{Deref, DerefMut};
// For correctness considerations, all uses of UnsafeCell can be ignored here; the only reason why
// an UnsafeCell is used is to indicate to the linker that a static mutex still needs to be
// allocated in .data and not in .text. (In other words: This is what allows transmuting the & to
// the inner data into a &mut).
use core::cell::UnsafeCell;

/// A mutual exclusion primitive useful for protecting shared data
///
/// Unlike the [std::sync::Mutex], this has no concept of poisoning, so waiting for mutexes in
/// panicked (and thus locked) threads will lock the accessing thread as well. This is because RIOT
/// threads don't unwind Rust code. As a consequence, the mutex interface is different from the
/// standard library's.
///
/// Several methods (into_inner, get_mut) are not implemented until they're actually needed.
///
/// [std::sync::mutex]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
pub struct Mutex<T> {
    mutex: UnsafeCell<riot_sys::inline::mutex_t>,
    data: UnsafeCell<T>,
}

impl<T> Mutex<T> {
    /// Create a new mutex in an unlocked state
    #[doc(alias = "mutex_init")]
    pub const fn new(t: T) -> Mutex<T> {
        // unsafe: Side effect free C macro
        let new = unsafe { riot_sys::macro_MUTEX_INIT() };
        Mutex {
            data: UnsafeCell::new(t),
            mutex: UnsafeCell::new(new),
        }
    }

    /// Get an accessor to the mutex when the mutex is available
    ///
    /// ## Panics
    ///
    /// This function checks at runtime whether it is called in a thread context, and panics
    /// otherwise. Consider promoting a reference with an [`InThread`](crate::thread::InThread)
    /// token's [`.promote(&my_mutex)`](crate::thread::InThread::promote) to gain access to a
    /// better [`.lock()`](crate::thread::ValueInThread<&Mutex<T>>::lock) method, which suffers
    /// neither the panic condition nor the runtime overhead.
    #[doc(alias = "mutex_lock")]
    pub fn lock(&self) -> MutexGuard<T> {
        crate::thread::InThread::new()
            .expect("Mutex::lock may only be called outside of interrupt contexts")
            .promote(self)
            .lock()
    }

    /// Get an accessor to the mutex if the mutex is available
    #[doc(alias = "mutex_trylock")]
    pub fn try_lock(&self) -> Option<MutexGuard<T>> {
        match unsafe { riot_sys::mutex_trylock(self.mutex.get()) } {
            1 => Some(MutexGuard { mutex: &self }),
            _ => None,
        }
    }

    /// Lock the mutex and throw away the key
    ///
    /// Try to lock the mutex (returning None if it is locked). When successful, a mutable
    /// reference for the complete lifetime of the mutex is produced, without the usual mechanisms
    /// that'd free the mutex later.
    ///
    /// This is an easy way to get a &'static mut reference in RIOT. Its downsides (compared to
    /// cortex-m-rt's entry mechanisms) are:
    ///
    /// * It has runtime storage cost (one mutex_t)
    /// * It has runtime processing cost (primarily the accompanying unwrap which the compiler
    ///   can't know to optimize out)
    /// * It needs a good default value (can be mitigated with MaybeUninit)
    ///
    /// but then again, it's easy.
    ///
    /// ## API rationale
    ///
    /// This requires access to the original mutex and not just an acquired guard that'd be leaked
    /// in the process: The latter could also be done on a more short-lived mutex, which would then
    /// be dropped (or even leaked-and-pushed-off-the-stack) even in a locked state. (A possibility
    /// that is fine -- we sure don't want to limit mutex usage to require a Pin reference.)
    ///
    /// The function could be generalized to some generic lifetime, but there doesn't seem to be a
    /// point to it.
    pub fn try_leak(&'static self) -> Option<&'static mut T> {
        let guard = self.try_lock()?;
        core::mem::forget(guard);
        Some(unsafe { &mut *self.data.get() })
    }
}

impl<'a, T> crate::thread::ValueInThread<&'a Mutex<T>> {
    /// Get an accessor to the mutex when the mutex is available
    ///
    /// Through the [crate::thread::ValueInThread], this is already guaranteed to run in a thread
    /// context, so no additional check is performed.
    #[doc(alias = "mutex_lock")]
    pub fn lock(self) -> MutexGuard<'a, T> {
        // unsafe: All preconditions of the C function are met (not-NULL through taking a &self,
        // being initialized through RAII guarantees, thread context is in the InThread).
        unsafe { riot_sys::mutex_lock(crate::inline_cast_mut(self.mutex.get())) };
        MutexGuard { mutex: &self }
    }
}

unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T: Send> Sync for Mutex<T> {}

impl<T: core::default::Default> core::default::Default for Mutex<T> {
    fn default() -> Self {
        Self::new(Default::default())
    }
}

/// A lock on a mutex
///
/// Though a MutexGuard, a mutex's inner value can be mutably accessed; the creation mechanism of
/// the locks ensures that only one MutexGuard is ever available for any given Mutex.
///
/// When the lock is dropped, the mutex becomes available again.
pub struct MutexGuard<'a, T> {
    mutex: &'a Mutex<T>,
}

impl<'a, T> Drop for MutexGuard<'a, T> {
    fn drop(&mut self) {
        unsafe { riot_sys::mutex_unlock(crate::inline_cast_mut(self.mutex.mutex.get())) }
    }
}

impl<'a, T> MutexGuard<'a, T> {
    /// Put the current thread to sleep right after unlocking the mutex. This is equivalent to
    /// calling mutex_unlock_and_sleep in RIOT.
    #[doc(alias = "mutex_unlock_and_sleep")]
    pub fn unlock_and_sleep(self) {
        let m = &self.mutex.mutex;
        ::core::mem::forget(self);
        unsafe { riot_sys::mutex_unlock_and_sleep(crate::inline_cast_mut(m.get())) };
    }
}

impl<'a, T> Deref for MutexGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe { &*self.mutex.data.get() }
    }
}

impl<'a, T> DerefMut for MutexGuard<'a, T> {
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *(self.mutex.data.get()) }
    }
}

impl<T> mutex_trait::Mutex for &Mutex<T> {
    type Data = T;

    fn lock<R>(&mut self, f: impl FnOnce(&mut Self::Data) -> R) -> R {
        f(&mut Mutex::lock(self))
    }
}