2022-06-15 10:24:36 +02:00
//! Async mutex.
//!
//! This module provides a mutex that can be used to synchronize data between asynchronous tasks.
2022-04-06 01:23:42 +02:00
use core ::cell ::{ RefCell , UnsafeCell } ;
use core ::ops ::{ Deref , DerefMut } ;
use core ::task ::Poll ;
2022-06-12 22:15:44 +02:00
2022-07-29 21:58:35 +02:00
use futures_util ::future ::poll_fn ;
2022-04-06 01:23:42 +02:00
use crate ::blocking_mutex ::raw ::RawMutex ;
use crate ::blocking_mutex ::Mutex as BlockingMutex ;
use crate ::waitqueue ::WakerRegistration ;
/// Error returned by [`Mutex::try_lock`]
#[ derive(PartialEq, Eq, Clone, Copy, Debug) ]
#[ cfg_attr(feature = " defmt " , derive(defmt::Format)) ]
pub struct TryLockError ;
struct State {
locked : bool ,
waker : WakerRegistration ,
}
2022-06-15 10:24:36 +02:00
/// Async mutex.
///
/// The mutex is generic over a blocking [`RawMutex`](crate::blocking_mutex::raw::RawMutex).
/// The raw mutex is used to guard access to the internal "is locked" flag. It
/// is held for very short periods only, while locking and unlocking. It is *not* held
/// for the entire time the async Mutex is locked.
///
/// Which implementation you select depends on the context in which you're using the mutex.
///
/// Use [`CriticalSectionRawMutex`](crate::blocking_mutex::raw::CriticalSectionRawMutex) when data can be shared between threads and interrupts.
///
/// Use [`NoopRawMutex`](crate::blocking_mutex::raw::NoopRawMutex) when data is only shared between tasks running on the same executor.
///
/// Use [`ThreadModeRawMutex`](crate::blocking_mutex::raw::ThreadModeRawMutex) when data is shared between tasks running on the same executor but you want a singleton.
///
2022-04-06 01:23:42 +02:00
pub struct Mutex < M , T >
where
M : RawMutex ,
T : ? Sized ,
{
state : BlockingMutex < M , RefCell < State > > ,
inner : UnsafeCell < T > ,
}
unsafe impl < M : RawMutex + Send , T : ? Sized + Send > Send for Mutex < M , T > { }
unsafe impl < M : RawMutex + Sync , T : ? Sized + Send > Sync for Mutex < M , T > { }
/// Async mutex.
impl < M , T > Mutex < M , T >
where
M : RawMutex ,
{
/// Create a new mutex with the given value.
pub const fn new ( value : T ) -> Self {
Self {
inner : UnsafeCell ::new ( value ) ,
state : BlockingMutex ::new ( RefCell ::new ( State {
locked : false ,
waker : WakerRegistration ::new ( ) ,
} ) ) ,
}
}
}
impl < M , T > Mutex < M , T >
where
M : RawMutex ,
T : ? Sized ,
{
/// Lock the mutex.
///
/// This will wait for the mutex to be unlocked if it's already locked.
pub async fn lock ( & self ) -> MutexGuard < '_ , M , T > {
poll_fn ( | cx | {
let ready = self . state . lock ( | s | {
let mut s = s . borrow_mut ( ) ;
if s . locked {
s . waker . register ( cx . waker ( ) ) ;
false
} else {
s . locked = true ;
true
}
} ) ;
if ready {
Poll ::Ready ( MutexGuard { mutex : self } )
} else {
Poll ::Pending
}
} )
. await
}
/// Attempt to immediately lock the mutex.
///
/// If the mutex is already locked, this will return an error instead of waiting.
pub fn try_lock ( & self ) -> Result < MutexGuard < '_ , M , T > , TryLockError > {
self . state . lock ( | s | {
let mut s = s . borrow_mut ( ) ;
if s . locked {
Err ( TryLockError )
} else {
s . locked = true ;
Ok ( ( ) )
}
} ) ? ;
Ok ( MutexGuard { mutex : self } )
}
}
/// Async mutex guard.
///
/// Owning an instance of this type indicates having
/// successfully locked the mutex, and grants access to the contents.
///
/// Dropping it unlocks the mutex.
pub struct MutexGuard < ' a , M , T >
where
M : RawMutex ,
T : ? Sized ,
{
mutex : & ' a Mutex < M , T > ,
}
impl < ' a , M , T > Drop for MutexGuard < ' a , M , T >
where
M : RawMutex ,
T : ? Sized ,
{
fn drop ( & mut self ) {
self . mutex . state . lock ( | s | {
let mut s = s . borrow_mut ( ) ;
s . locked = false ;
s . waker . wake ( ) ;
} )
}
}
impl < ' a , M , T > Deref for MutexGuard < ' a , M , T >
where
M : RawMutex ,
T : ? Sized ,
{
type Target = T ;
fn deref ( & self ) -> & Self ::Target {
// Safety: the MutexGuard represents exclusive access to the contents
// of the mutex, so it's OK to get it.
unsafe { & * ( self . mutex . inner . get ( ) as * const T ) }
}
}
impl < ' a , M , T > DerefMut for MutexGuard < ' a , M , T >
where
M : RawMutex ,
T : ? Sized ,
{
fn deref_mut ( & mut self ) -> & mut Self ::Target {
// Safety: the MutexGuard represents exclusive access to the contents
// of the mutex, so it's OK to get it.
unsafe { & mut * ( self . mutex . inner . get ( ) ) }
}
}