use core::{
cell::UnsafeCell,
fmt,
marker::PhantomData,
mem::MaybeUninit,
ops::{Deref, DerefMut},
};
use crate::{
hunk::Hunk,
kernel::{
mutex, prelude::*, traits, Cfg, LockMutexError, MarkConsistentMutexError, MutexProtocol,
TryLockMutexError,
},
sync::source::{DefaultSource, Source},
utils::Init,
};
#[doc = include_str!("../common.md")]
pub struct Definer<System, Source> {
mutex: mutex::MutexDefiner<System>,
source: Source,
}
pub struct GenericMutex<Cell, Mutex> {
cell: Cell,
mutex: Mutex,
}
#[doc = crate::tests::doc_test!(
/// ```rust
/// use r3::{kernel::StaticTask, sync::StaticMutex};
///
/// struct Objects {
/// task2: StaticTask<System>,
/// mutex: StaticMutex<System, i32>,
/// }
///
/// const fn configure_app<C>(cfg: &mut Cfg<C>) -> Objects
/// where
/// C: ~const traits::CfgTask<System = System> +
/// ~const traits::CfgMutex,
/// {
/// StaticTask::define()
/// .start(task1_body)
/// .priority(2)
/// .active(true)
/// .finish(cfg);
///
/// let task2 = StaticTask::define()
/// .start(task2_body)
/// .priority(1)
/// .finish(cfg);
///
/// let mutex = StaticMutex::define().init(|| 1).finish(cfg);
///
/// Objects { task2, mutex }
/// }
///
/// fn task1_body() {
/// let mut guard = COTTAGE.mutex.lock().unwrap();
///
/// // Although `task2` has a higher priority, it's unable to
/// // access `*guard` until `task1` releases the lock
/// COTTAGE.task2.activate().unwrap();
///
/// assert_eq!(*guard, 1);
/// *guard = 2;
/// }
///
/// fn task2_body() {
/// let mut guard = COTTAGE.mutex.lock().unwrap();
/// assert_eq!(*guard, 2);
/// *guard = 3;
/// # exit(0);
/// }
/// ```
)]
pub type StaticMutex<System, T> =
GenericMutex<Hunk<System, UnsafeCell<MaybeUninit<T>>>, mutex::StaticMutex<System>>;
unsafe impl<Cell, Mutex, T: Send> Send for GenericMutex<Cell, Mutex> where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>
{
}
unsafe impl<Cell, Mutex, T: Send> Sync for GenericMutex<Cell, Mutex> where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>
{
}
#[must_use = "if unused the GenericMutex will immediately unlock"]
pub struct GenericMutexGuard<'a, Cell, Mutex: mutex::MutexHandle> {
mutex: &'a GenericMutex<Cell, Mutex>,
_no_send_sync: PhantomData<*mut ()>,
}
pub type StaticMutexGuard<'a, System, T> =
GenericMutexGuard<'a, Hunk<System, UnsafeCell<MaybeUninit<T>>>, mutex::MutexRef<'a, System>>;
unsafe impl<Cell, Mutex, T: Sync> Sync for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
}
pub type LockResult<Guard> = Result<Guard, LockError<Guard>>;
pub type TryLockResult<Guard> = Result<Guard, TryLockError<Guard>>;
#[repr(i8)]
pub enum LockError<Guard> {
BadContext = LockMutexError::BadContext as i8,
Interrupted = LockMutexError::Interrupted as i8,
WouldDeadlock = LockMutexError::WouldDeadlock as i8,
BadParam = LockMutexError::BadParam as i8,
Abandoned(Guard) = LockMutexError::Abandoned as i8,
}
impl<Guard> fmt::Debug for LockError<Guard> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::BadContext => "BadContext",
Self::Interrupted => "Interrupted",
Self::WouldDeadlock => "WouldDeadlock",
Self::BadParam => "BadParam",
Self::Abandoned(_) => "Abandoned",
})
}
}
#[repr(i8)]
pub enum TryLockError<Guard> {
BadContext = TryLockMutexError::BadContext as i8,
WouldDeadlock = LockMutexError::WouldDeadlock as i8,
WouldBlock = TryLockMutexError::Timeout as i8,
BadParam = TryLockMutexError::BadParam as i8,
Abandoned(Guard) = TryLockMutexError::Abandoned as i8,
}
impl<Guard> fmt::Debug for TryLockError<Guard> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::BadContext => "BadContext",
Self::WouldBlock => "WouldBlock",
Self::WouldDeadlock => "WouldDeadlock",
Self::BadParam => "BadParam",
Self::Abandoned(_) => "Abandoned",
})
}
}
#[derive(Debug)]
#[repr(i8)]
pub enum MarkConsistentError {
BadContext = MarkConsistentMutexError::BadContext as i8,
Consistent = MarkConsistentMutexError::BadObjectState as i8,
}
impl<System, T: 'static> StaticMutex<System, T>
where
System: traits::KernelMutex + traits::KernelStatic,
{
pub const fn define() -> Definer<System, DefaultSource<T>> {
Definer {
mutex: mutex::StaticMutex::define(),
source: DefaultSource::INIT, }
}
}
impl<System, Source> Definer<System, Source>
where
System: traits::KernelMutex,
{
pub const fn protocol(self, protocol: MutexProtocol) -> Self {
Self {
mutex: self.mutex.protocol(protocol),
..self
}
}
}
impl_source_setter!(
#[no_autowrap()]
impl Definer<System, #Source>
);
impl<System, Source> Definer<System, Source>
where
System: traits::KernelMutex + traits::KernelStatic,
{
pub const fn finish<C: ~const traits::CfgMutex<System = System>>(
self,
cfg: &mut Cfg<C>,
) -> StaticMutex<System, Source::Target>
where
Source: ~const self::Source<System>,
{
GenericMutex {
cell: self.source.into_unsafe_cell_hunk(cfg),
mutex: self.mutex.finish(cfg),
}
}
}
impl<Cell, Mutex, T> GenericMutex<Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
pub fn lock(&self) -> LockResult<GenericMutexGuard<'_, Cell, Mutex>> {
match self.mutex.lock() {
Ok(()) => Ok(GenericMutexGuard {
mutex: self,
_no_send_sync: PhantomData,
}),
Err(LockMutexError::NoAccess) => unreachable!(),
Err(LockMutexError::BadContext) => Err(LockError::BadContext),
Err(LockMutexError::Interrupted) => Err(LockError::Interrupted),
Err(LockMutexError::WouldDeadlock) => Err(LockError::WouldDeadlock),
Err(LockMutexError::BadParam) => Err(LockError::BadParam),
Err(LockMutexError::Abandoned) => Err(LockError::Abandoned(GenericMutexGuard {
mutex: self,
_no_send_sync: PhantomData,
})),
}
}
pub fn try_lock(&self) -> TryLockResult<GenericMutexGuard<'_, Cell, Mutex>> {
match self.mutex.try_lock() {
Ok(()) => Ok(GenericMutexGuard {
mutex: self,
_no_send_sync: PhantomData,
}),
Err(TryLockMutexError::NoAccess) => unreachable!(),
Err(TryLockMutexError::BadContext) => Err(TryLockError::BadContext),
Err(TryLockMutexError::WouldDeadlock) => Err(TryLockError::WouldDeadlock),
Err(TryLockMutexError::Timeout) => Err(TryLockError::WouldBlock),
Err(TryLockMutexError::BadParam) => Err(TryLockError::BadParam),
Err(TryLockMutexError::Abandoned) => Err(TryLockError::Abandoned(GenericMutexGuard {
mutex: self,
_no_send_sync: PhantomData,
})),
}
}
pub fn mark_consistent(&self) -> Result<(), MarkConsistentError> {
self.mutex.mark_consistent().map_err(|e| match e {
MarkConsistentMutexError::NoAccess => unreachable!(),
MarkConsistentMutexError::BadContext => MarkConsistentError::BadContext,
MarkConsistentMutexError::BadObjectState => MarkConsistentError::Consistent,
})
}
#[inline]
pub fn get_ptr(&self) -> *mut T {
self.cell.get().cast()
}
}
impl<Cell, Mutex, T: fmt::Debug> fmt::Debug for GenericMutex<Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.try_lock() {
Ok(guard) => f
.debug_struct("GenericMutex")
.field("data", &&*guard)
.finish(),
Err(TryLockError::BadContext) => {
struct BadContextPlaceholder;
impl fmt::Debug for BadContextPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<bad context>")
}
}
f.debug_struct("GenericMutex")
.field("data", &BadContextPlaceholder)
.finish()
}
Err(TryLockError::WouldBlock | TryLockError::WouldDeadlock) => {
struct LockedPlaceholder;
impl fmt::Debug for LockedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<locked>")
}
}
f.debug_struct("GenericMutex")
.field("data", &LockedPlaceholder)
.finish()
}
Err(TryLockError::Abandoned(_)) => {
struct AbandonedPlaceholder;
impl fmt::Debug for AbandonedPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<abandoned>")
}
}
f.debug_struct("GenericMutex")
.field("data", &AbandonedPlaceholder)
.finish()
}
Err(TryLockError::BadParam) => {
struct BadParamPlaceholder;
impl fmt::Debug for BadParamPlaceholder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("<current priority too high>")
}
}
f.debug_struct("GenericMutex")
.field("data", &BadParamPlaceholder)
.finish()
}
}
}
}
impl<Cell, Mutex, T: fmt::Debug> fmt::Debug for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
impl<Cell, Mutex, T: fmt::Display> fmt::Display for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&**self, f)
}
}
impl<Cell, Mutex> Drop for GenericMutexGuard<'_, Cell, Mutex>
where
Mutex: mutex::MutexHandle,
{
#[inline]
fn drop(&mut self) {
self.mutex.mutex.unlock().unwrap();
}
}
impl<Cell, Mutex, T> Deref for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { (*self.mutex.cell.get()).assume_init_ref() }
}
}
impl<Cell, Mutex, T> DerefMut for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { (*self.mutex.cell.get()).assume_init_mut() }
}
}
unsafe impl<Cell, Mutex, T> stable_deref_trait::StableDeref for GenericMutexGuard<'_, Cell, Mutex>
where
Cell: Deref<Target = UnsafeCell<MaybeUninit<T>>>,
Mutex: mutex::MutexHandle,
{
}