rt 0.19.1

A real-time operating system capable of full preemption
Documentation
use core::{
    cell::UnsafeCell,
    marker::PhantomData,
    ops::{Deref, DerefMut},
    panic::{RefUnwindSafe, UnwindSafe},
};

use crate::{
    bindings::{
        RT_MUTEX_LEVEL_NONRECURSIVE, rt_mutex, rt_mutex_lock, rt_mutex_timedlock, rt_mutex_trylock,
        rt_mutex_unlock,
    },
    list::list_init,
    ptr_macros::{ptr_to_field, ptr_to_field_mut},
    tick::Utick,
};

pub struct Mutex<T: ?Sized> {
    mutex: UnsafeCell<rt_mutex>,
    data: UnsafeCell<T>,
}

unsafe impl<T: ?Sized + Send> Send for Mutex<T> {}
unsafe impl<T: ?Sized + Send> Sync for Mutex<T> {}
impl<T: ?Sized> UnwindSafe for Mutex<T> {}
impl<T: ?Sized> RefUnwindSafe for Mutex<T> {}

impl<T> Mutex<T> {
    /// Initialize a new `Mutex` with the given data.
    ///
    /// # Safety
    ///
    /// This must be called with a pointer to a `static` `Mutex` and be used to initialize that
    /// same `Mutex`. Users should use the `rt::sync::mutex!` macro to create a `Mutex` rather than
    /// call `Mutex::init` directly.
    #[must_use]
    pub const unsafe fn init(this: *const Self, t: T) -> Mutex<T> {
        let mutex = UnsafeCell::raw_get(ptr_to_field!(this, mutex));
        Mutex {
            mutex: UnsafeCell::new(rt_mutex {
                holder: 0,
                wait_list: list_init(ptr_to_field_mut!(mutex, wait_list)),
                list: list_init(ptr_to_field_mut!(mutex, list)),
                level: RT_MUTEX_LEVEL_NONRECURSIVE,
            }),
            data: UnsafeCell::new(t),
        }
    }
}

impl<T: ?Sized> Mutex<T> {
    /// Acquires the mutex, blocking the current task until the mutex is available. If the calling
    /// task has higher priority than the task currently holding the mutex, the caller will donate
    /// its priority level to the holding task until the mutex is released.
    #[inline]
    pub fn lock(&self) -> MutexGuard<'_, T> {
        unsafe { rt_mutex_lock(self.mutex.get()) }
        MutexGuard::new(self)
    }

    /// Attempts to acquire the mutex. If the mutex is already held, `None` is returned.
    #[inline]
    pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
        if unsafe { rt_mutex_trylock(self.mutex.get()) } {
            Some(MutexGuard::new(self))
        } else {
            None
        }
    }

    /// Attempts to acquire the mutex, waiting for `ticks` to elapse before giving up. If the mutex
    /// is still held, `None` is returned.
    #[inline]
    pub fn timed_lock(&self, ticks: Utick) -> Option<MutexGuard<'_, T>> {
        if unsafe { rt_mutex_timedlock(self.mutex.get(), ticks) } {
            Some(MutexGuard::new(self))
        } else {
            None
        }
    }

    // Probably not useful because this Mutex must be static.
    #[inline]
    pub fn get_mut(&mut self) -> &mut T {
        self.data.get_mut()
    }
}

pub struct MutexGuard<'a, T: ?Sized + 'a> {
    lock: &'a Mutex<T>,
    _unsend_marker: PhantomData<*const ()>,
}

impl<T: ?Sized> MutexGuard<'_, T> {
    #[inline]
    const fn new(lock: &Mutex<T>) -> MutexGuard<'_, T> {
        MutexGuard {
            lock,
            _unsend_marker: PhantomData,
        }
    }
}

// Not supported yet, so using the _unsend_marker instead.
//impl<T: ?Sized> !Send for MutexGuard<'_, T> {}

unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}

impl<T: ?Sized> MutexGuard<'_, T> {
    // Needs to be pub(crate) so that cond can use it.
    #[inline]
    pub(crate) fn ptr(&self) -> *mut rt_mutex {
        self.lock.mutex.get()
    }

    /// Use `unlock` only if you need to explicitly release the Mutex associated with the MutexGuard
    /// before it would go out of scope and unlock itself.
    #[inline]
    pub fn unlock(self) {
        drop(self)
    }
}

impl<T: ?Sized> Deref for MutexGuard<'_, T> {
    type Target = T;

    #[inline]
    fn deref(&self) -> &T {
        unsafe { &*self.lock.data.get() }
    }
}

impl<T: ?Sized> DerefMut for MutexGuard<'_, T> {
    #[inline]
    fn deref_mut(&mut self) -> &mut T {
        unsafe { &mut *self.lock.data.get() }
    }
}

impl<T: ?Sized> Drop for MutexGuard<'_, T> {
    #[inline]
    fn drop(&mut self) {
        unsafe { rt_mutex_unlock(self.lock.mutex.get()) }
    }
}

#[macro_export]
macro_rules! mutex {
    ($name: ident, $type: ty, $data: expr) => {
        static $name: $crate::sync::Mutex<$type> = {
            let ptr = &raw const $name;
            let data = $data;
            unsafe { $crate::sync::Mutex::init(ptr, data) }
        };
    };

    ($name: ident) => {
        rt::mutex!($name, (), ());
    };
}

#[cfg(test)]
mod tests {
    use crate::test_init;

    #[test]
    fn fast_path() {
        test_init();
        mutex!(MUTEX, i32, 0);
        *MUTEX.lock() += 1;
        assert_eq!(*MUTEX.lock(), 1);
    }

    #[test]
    fn try_lock() {
        test_init();
        mutex!(MUTEX, i32, 0);
        if let Some(mut x) = MUTEX.try_lock() {
            *x += 1;
        }
        assert_eq!(*MUTEX.lock(), 1);
    }
}