use core::{cell::UnsafeCell, fmt::Debug, mem};
use crate::error::take_errno;
pub struct Mutex<T> {
pros_mutex: pros_sys::mutex_t,
data: Option<UnsafeCell<T>>,
}
unsafe impl<T: Send> Send for Mutex<T> {}
unsafe impl<T> Sync for Mutex<T> {}
impl<T> Mutex<T> {
pub fn new(data: T) -> Self {
let pros_mutex = unsafe { pros_sys::mutex_create() };
Self {
pros_mutex,
data: Some(UnsafeCell::new(data)),
}
}
pub fn lock(&self) -> MutexGuard<'_, T> {
if !unsafe { pros_sys::mutex_take(self.pros_mutex, pros_sys::TIMEOUT_MAX) } {
panic!("Mutex lock failed: {}", take_errno());
}
MutexGuard { mutex: self }
}
pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
let success = unsafe { pros_sys::mutex_take(self.pros_mutex, 0) };
success.then(|| MutexGuard::new(self))
}
pub fn into_inner(mut self) -> T {
let data = mem::take(&mut self.data).unwrap();
data.into_inner()
}
pub fn get_mut(&mut self) -> &mut T {
self.data.as_mut().unwrap().get_mut()
}
}
impl<T> Drop for Mutex<T> {
fn drop(&mut self) {
unsafe {
pros_sys::mutex_delete(self.pros_mutex);
}
}
}
impl<T> Debug for Mutex<T>
where
T: Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
struct Placeholder;
impl Debug for Placeholder {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("<locked>")
}
}
let mut d = f.debug_struct("Mutex");
match self.try_lock() {
Some(guard) => d.field("data", &&*guard),
None => d.field("data", &Placeholder),
};
d.finish_non_exhaustive()
}
}
impl<T> Default for Mutex<T>
where
T: Default,
{
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> From<T> for Mutex<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
#[derive(Debug)]
pub struct MutexGuard<'a, T> {
mutex: &'a Mutex<T>,
}
impl<'a, T> MutexGuard<'a, T> {
const fn new(mutex: &'a Mutex<T>) -> Self {
Self { mutex }
}
}
impl<T> core::ops::Deref for MutexGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.mutex.data.as_ref().unwrap().get() }
}
}
impl<T> core::ops::DerefMut for MutexGuard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.mutex.data.as_ref().unwrap().get() }
}
}
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
unsafe {
pros_sys::mutex_give(self.mutex.pros_mutex);
}
}
}