pros_core/
sync.rs

1//! Synchronization types for FreeRTOS tasks.
2//!
3//! Types implemented here are specifically designed to mimic the standard library.
4
5use core::{cell::UnsafeCell, fmt::Debug, mem};
6
7use crate::error::take_errno;
8
9/// The basic mutex type.
10/// Mutexes are used to share variables between tasks safely.
11pub struct Mutex<T> {
12    pros_mutex: pros_sys::mutex_t,
13    data: Option<UnsafeCell<T>>,
14}
15unsafe impl<T: Send> Send for Mutex<T> {}
16unsafe impl<T> Sync for Mutex<T> {}
17
18impl<T> Mutex<T> {
19    /// Creates a new mutex.
20    pub fn new(data: T) -> Self {
21        let pros_mutex = unsafe { pros_sys::mutex_create() };
22
23        Self {
24            pros_mutex,
25            data: Some(UnsafeCell::new(data)),
26        }
27    }
28
29    /// Locks the mutex so that it cannot be locked in another task at the same time.
30    /// Blocks the current task until the lock is acquired.
31    pub fn lock(&self) -> MutexGuard<'_, T> {
32        if !unsafe { pros_sys::mutex_take(self.pros_mutex, pros_sys::TIMEOUT_MAX) } {
33            panic!("Mutex lock failed: {}", take_errno());
34        }
35
36        MutexGuard { mutex: self }
37    }
38
39    /// Attempts to acquire this lock. This function does not block.
40    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
41        let success = unsafe { pros_sys::mutex_take(self.pros_mutex, 0) };
42        success.then(|| MutexGuard::new(self))
43    }
44
45    /// Consumes the mutex and returns the inner data.
46    pub fn into_inner(mut self) -> T {
47        let data = mem::take(&mut self.data).unwrap();
48        data.into_inner()
49    }
50
51    /// Gets a mutable reference to the inner data.
52    pub fn get_mut(&mut self) -> &mut T {
53        self.data.as_mut().unwrap().get_mut()
54    }
55}
56
57impl<T> Drop for Mutex<T> {
58    fn drop(&mut self) {
59        unsafe {
60            pros_sys::mutex_delete(self.pros_mutex);
61        }
62    }
63}
64
65impl<T> Debug for Mutex<T>
66where
67    T: Debug,
68{
69    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70        struct Placeholder;
71        impl Debug for Placeholder {
72            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
73                f.write_str("<locked>")
74            }
75        }
76
77        let mut d = f.debug_struct("Mutex");
78        match self.try_lock() {
79            Some(guard) => d.field("data", &&*guard),
80            None => d.field("data", &Placeholder),
81        };
82        d.finish_non_exhaustive()
83    }
84}
85
86impl<T> Default for Mutex<T>
87where
88    T: Default,
89{
90    fn default() -> Self {
91        Self::new(T::default())
92    }
93}
94
95impl<T> From<T> for Mutex<T> {
96    fn from(value: T) -> Self {
97        Self::new(value)
98    }
99}
100
101/// Allows the user to access the data from a locked mutex.
102/// Dereference to get the inner data.
103#[derive(Debug)]
104pub struct MutexGuard<'a, T> {
105    mutex: &'a Mutex<T>,
106}
107
108impl<'a, T> MutexGuard<'a, T> {
109    const fn new(mutex: &'a Mutex<T>) -> Self {
110        Self { mutex }
111    }
112}
113
114impl<T> core::ops::Deref for MutexGuard<'_, T> {
115    type Target = T;
116    fn deref(&self) -> &T {
117        unsafe { &*self.mutex.data.as_ref().unwrap().get() }
118    }
119}
120
121impl<T> core::ops::DerefMut for MutexGuard<'_, T> {
122    fn deref_mut(&mut self) -> &mut T {
123        unsafe { &mut *self.mutex.data.as_ref().unwrap().get() }
124    }
125}
126
127impl<T> Drop for MutexGuard<'_, T> {
128    fn drop(&mut self) {
129        unsafe {
130            pros_sys::mutex_give(self.mutex.pros_mutex);
131        }
132    }
133}