vex_rt/rtos/
mutex.rs

1use core::{
2    cell::UnsafeCell,
3    fmt::{self, Debug, Display, Formatter},
4    ops::{Deref, DerefMut},
5};
6
7use crate::{bindings, error::*};
8
9use super::TIMEOUT_MAX;
10
11/// Represents an object which is protected by a FreeRTOS recursive mutex.
12pub struct Mutex<T: ?Sized> {
13    mutex: bindings::mutex_t,
14    data: UnsafeCell<T>,
15}
16
17unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
18
19unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
20
21/// Provides a constant-period looping construct.
22impl<T> Mutex<T> {
23    #[inline]
24    /// Creates a new mutex which wraps the given object. Panics on failure; see
25    /// [`Mutex::try_new()`].
26    pub fn new(data: T) -> Self {
27        Self::try_new(data).unwrap_or_else(|err| panic!("failed to create mutex: {:?}", err))
28    }
29
30    /// Creates a new mutex which wraps the given object.
31    pub fn try_new(data: T) -> Result<Self, Error> {
32        Ok(Self {
33            data: UnsafeCell::new(data),
34            mutex: unsafe { bindings::mutex_recursive_create() }.check()?,
35        })
36    }
37}
38
39impl<T: ?Sized> Mutex<T> {
40    #[inline]
41    /// Obtains a [`MutexGuard`] giving access to the object protected by the
42    /// mutex. Blocks until access can be obtained. Panics on failure; see
43    /// [`Mutex::try_lock()`].
44    ///
45    /// # Behaviour
46    ///
47    /// This function is blocking; it does not return until the the mutex is
48    /// taken by the task, and allows other tasks to run in the meantime.
49    /// Further, the task which currently holds the mutex actually inherits the
50    /// current task's priority, if it is higher; this prevents a high-priority
51    /// task from being slowed down by waiting for a resource held by a
52    /// low-priority task. See [this documentation from
53    /// FreeRTOS](https://www.freertos.org/Real-time-embedded-RTOS-mutexes.html)
54    /// for details.
55    pub fn lock(&'_ self) -> MutexGuard<'_, T> {
56        self.try_lock()
57            .unwrap_or_else(|err| panic!("Failed to lock mutex: {:?}", err))
58    }
59
60    #[inline]
61    /// Obtains a [`MutexGuard`] giving access to the object protected by the
62    /// mutex. Blocks until access can be obtained; see [`Mutex::lock()`] for a
63    /// more thorough behavioural description.
64    pub fn try_lock(&'_ self) -> Result<MutexGuard<'_, T>, Error> {
65        if unsafe { bindings::mutex_recursive_take(self.mutex, TIMEOUT_MAX) } {
66            Ok(MutexGuard(self))
67        } else {
68            Err(from_errno())
69        }
70    }
71
72    #[inline]
73    /// Obtains a [`MutexGuard`] giving access to the object protected by the
74    /// mutex, if it is available immediately. Does not block.
75    pub fn poll(&'_ self) -> Option<MutexGuard<'_, T>> {
76        if unsafe { bindings::mutex_recursive_take(self.mutex, 0) } {
77            Some(MutexGuard(self))
78        } else {
79            None
80        }
81    }
82}
83
84impl<T: ?Sized> Drop for Mutex<T> {
85    #[inline]
86    fn drop(&mut self) {
87        unsafe { bindings::mutex_delete(self.mutex) }
88    }
89}
90
91impl<T: ?Sized + Debug> Debug for Mutex<T> {
92    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
93        match self.poll() {
94            Some(guard) => f.debug_struct("Mutex").field("data", &&*guard).finish(),
95            None => {
96                struct LockedPlaceholder;
97                impl Debug for LockedPlaceholder {
98                    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
99                        f.write_str("<locked>")
100                    }
101                }
102
103                f.debug_struct("Mutex")
104                    .field("data", &LockedPlaceholder)
105                    .finish()
106            }
107        }
108    }
109}
110
111impl<T: ?Sized + Default> Default for Mutex<T> {
112    #[inline]
113    fn default() -> Self {
114        Self::new(Default::default())
115    }
116}
117
118impl<T> From<T> for Mutex<T> {
119    #[inline]
120    fn from(data: T) -> Self {
121        Self::new(data)
122    }
123}
124
125/// Provides exclusive access to an object controlled by a [`Mutex`] via the
126/// RAII pattern.
127///
128/// # Behaviour
129///
130/// This object represents the current task's ownership of the mutex. As such,
131/// it explicitly does not implement the [`Send`] trait---meaning that this
132/// ownership cannot be transferred to another task---and its destructor, via
133/// the [`Drop`] trait, ensures that the mutex is released when the object goes
134/// out of scope. Rust's object and reference lifetime rules prevent safe code
135/// from retaining access to the [`Mutex`] object's internal data beyond the
136/// lifetime of the guard object.
137pub struct MutexGuard<'a, T: ?Sized>(&'a Mutex<T>);
138
139impl<T: ?Sized> Deref for MutexGuard<'_, T> {
140    type Target = T;
141
142    #[inline]
143    fn deref(&self) -> &Self::Target {
144        unsafe { &*self.0.data.get() }
145    }
146}
147
148impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
149    #[inline]
150    fn deref_mut(&mut self) -> &mut Self::Target {
151        unsafe { &mut *self.0.data.get() }
152    }
153}
154
155impl<T: ?Sized> Drop for MutexGuard<'_, T> {
156    #[inline]
157    fn drop(&mut self) {
158        if !unsafe { bindings::mutex_recursive_give(self.0.mutex) } {
159            panic!("failed to return mutex: {:?}", from_errno());
160        }
161    }
162}
163
164impl<T: ?Sized + Debug> Debug for MutexGuard<'_, T> {
165    #[inline]
166    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167        Debug::fmt(&**self, f)
168    }
169}
170
171impl<T: ?Sized + Display> Display for MutexGuard<'_, T> {
172    #[inline]
173    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174        (**self).fmt(f)
175    }
176}
177
178impl<T: ?Sized> !Send for MutexGuard<'_, T> {}
179
180unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}