rttrust 0.1.3

Rust wrapper for rt-thread
Documentation
//! Thread synchronizes through the acquisition and release of semaphore and mutexes
//!
//! RT-Thread uses thread semaphores, mutexes, and event sets to achieve inter-thread synchronization. Thread synchronizes through the acquisition and release of semaphore and mutexes; the mutex uses priority inheritance to solve the common priority inversion problem in the real-time system. The thread synchronization mechanism allows threads to wait according to priorities or to acquire semaphores or mutexes following the first-in first-out method. Threads synchronize through sending and receiving of events; event sets allows "or trigger" and "and trigger" for multiple events, suitable for situations where threads are waiting for multiple events.

//! The concepts of semaphores, mutexes, and event sets are detailed in the "Inter-Thread Synchronization" chapter.
//! ### TODO
//! 1. rt_event

pub mod mutex;
pub mod sem;

use crate::ffi::{rt_enter_critical, rt_exit_critical, RT_IPC_FLAG_FIFO, RT_IPC_FLAG_PRIO};
use crate::Result;
use core::{
    cell::UnsafeCell,
    fmt,
    mem::MaybeUninit,
    ops::{Deref, DerefMut},
    pin::Pin,
    sync::atomic::{AtomicUsize, Ordering},
};

#[derive(Copy, Clone)]
pub enum IpcFlag {
    Fifo,
    Priority,
}

impl From<IpcFlag> for u8 {
    #[inline]
    fn from(flag: IpcFlag) -> Self {
        (match flag {
            IpcFlag::Fifo => RT_IPC_FLAG_FIFO,
            IpcFlag::Priority => RT_IPC_FLAG_PRIO,
        }) as u8
    }
}

#[must_use = "if unused the SchedulerLock will immediately unlock"]
pub struct SchedulerLock<T: ?Sized> {
    data: UnsafeCell<T>,
}

pub struct SchedulerLockGuard<'a, T: ?Sized + 'a> {
    lock: &'a SchedulerLock<T>,
}

unsafe impl<T: ?Sized + Send> Send for SchedulerLock<T> {}
unsafe impl<T: ?Sized + Send> Sync for SchedulerLock<T> {}

impl<T> SchedulerLock<T> {
    pub const fn new(data: T) -> Self {
        Self {
            data: UnsafeCell::new(data),
        }
    }

    pub fn lock(&self) -> SchedulerLockGuard<'_, T> {
        unsafe {
            rt_enter_critical();
        }
        SchedulerLockGuard { lock: self }
    }

    pub fn into_inner(self) -> Result<T>
    where
        T: Sized,
    {
        // We know statically that there are no outstanding references to
        // `self` so there's no need to lock the inner mutex.
        Ok(self.data.into_inner())
    }

    pub fn get_mut(&mut self) -> Result<&mut T> {
        // We know statically that there are no other references to `self`, so
        // there's no need to lock the inner mutex.
        Ok(unsafe { &mut *self.data.get() })
    }
}

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

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.lock.data.get() }
    }
}

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

const UNINIT: usize = 0;
const UNOWEND: usize = 1;
const OWEND: usize = 2;

pub struct Static<T> {
    state: AtomicUsize,
    data: UnsafeCell<MaybeUninit<T>>,
}

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

#[must_use = "if unused the StaticOwned will immediately unlock"]
pub struct StaticOwned<'lock, T> {
    lock: &'lock Static<T>,
}

impl<T> Static<T> {
    pub const fn new() -> Self {
        Self {
            state: AtomicUsize::new(UNINIT),
            data: UnsafeCell::new(MaybeUninit::uninit()),
        }
    }

    pub fn try_own(&self) -> Option<StaticOwned<T>>
    where
        T: Default,
    {
        let state = self.state.load(Ordering::SeqCst);
        if state == OWEND {
            return None;
        }
        let state = self.state.compare_and_swap(state, OWEND, Ordering::SeqCst);

        match state {
            OWEND => return None,
            UNINIT => unsafe {
                *self.data.get() = MaybeUninit::new(T::default());
            },
            _ => (),
        }

        Some(StaticOwned::new(self))
    }

    pub fn try_own_uninit(&self) -> Option<StaticOwned<T>> {
        let state = self.state.load(Ordering::SeqCst);
        if state == OWEND {
            return None;
        }
        let state = self.state.compare_and_swap(state, OWEND, Ordering::SeqCst);

        match state {
            OWEND => return None,
            _ => (),
        }

        Some(StaticOwned::new(self))
    }
}

impl<'lock, T> StaticOwned<'lock, T> {
    fn new(lock: &'lock Static<T>) -> Self {
        Self { lock }
    }

    pub fn get_ref(&self) -> Pin<&'lock T> {
        unsafe { Pin::new_unchecked(&*(&*self.lock.data.get()).as_ptr()) }
    }
    
    pub fn get_mut(&mut self) -> Pin<&'lock mut T> {
        unsafe { Pin::new_unchecked(&mut *(&mut *self.lock.data.get()).as_mut_ptr()) }
    }
}
impl<T> Drop for StaticOwned<'_, T> {
    #[inline]
    fn drop(&mut self) {
        self.lock.state.store(UNOWEND, Ordering::SeqCst)
    }
}

impl<T: fmt::Debug> fmt::Debug for StaticOwned<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get_ref().fmt(f)
    }
}

impl<T: fmt::Display> fmt::Display for StaticOwned<'_, T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get_ref().fmt(f)
    }
}