#![allow(unsafe_code)] //! Deadlock-free mutex, generic over lock level and backend. //! //! [`Mutex`] wraps a [`RawMutex`] //! implementation and a `T`, tagging the pair with a lock level. //! All ordered locking goes through //! [`MutexKey`](crate::key::MutexKey) + //! [`LockSet`](crate::set::LockSet) -- there is no public `lock()` //! method by default. //! //! The level parameter `Lvl` defaults to [`Base`] (= `Level<0>`). //! The backend parameter `R` defaults to //! [`StdMutex`](crate::raw_mutex::std_mutex::StdMutex) when the `std` //! feature is enabled (which it is by default). //! //! Users can specify just the level without naming the backend: //! `Mutex>`. pub mod guard; use core::{cell::UnsafeCell, fmt, marker::PhantomData}; use crate::{id::LockId, level::IsLevel, raw_mutex::RawMutex}; #[cfg(feature = "std")] use crate::level::Base; #[cfg(feature = "escape-hatch")] use guard::MutexGuard; /// A deadlock-free mutex generic over lock level and backend. /// /// `T` is the protected data. `Lvl` defaults to [`Base`] (= /// `Level<0>`) -- levels are opt-in for incremental cross-level /// acquisition. `R` defaults to /// [`StdMutex`](crate::raw_mutex::std_mutex::StdMutex) on `std`. /// /// Specify just the level to use the default backend: /// `Mutex>`. /// /// All ordered locking goes through /// [`MutexKey::lock`](crate::key::MutexKey::lock). There is no public /// `lock()` method unless the `escape-hatch` feature is enabled. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, mutex::Mutex}; /// /// let counter: Mutex = Mutex::new(0); /// /// lock_scope(|key| { /// let (mut guard, _key) = key.lock(&counter); /// *guard += 1; /// }); /// ``` #[cfg(feature = "std")] pub struct Mutex { id: LockId, pub(crate) raw: R, pub(crate) data: UnsafeCell, _level: PhantomData, } /// A deadlock-free mutex generic over lock level and backend. /// /// `T` is the protected data. `Lvl` defaults to [`Base`] (= /// `Level<0>`). `R` is any [`RawMutex`] implementation. /// /// Enable the `std` feature (on by default) for a default `R` /// parameter, allowing `Mutex` or `Mutex>` /// without naming the backend. /// /// # Examples /// /// ```rust,ignore /// use surelock::{key::lock_scope, mutex::Mutex}; /// /// type M = Mutex>; /// let counter: M = Mutex::new(0); /// ``` #[cfg(not(feature = "std"))] pub struct Mutex { id: LockId, pub(crate) raw: R, pub(crate) data: UnsafeCell, _level: PhantomData, } impl Mutex { /// Create a new mutex with the given data. /// /// A unique [`LockId`] is assigned from a global atomic counter. #[must_use] pub fn new(data: T) -> Self { Self { id: LockId::next(), raw: RawMutex::new(), data: UnsafeCell::new(data), _level: PhantomData, } } /// Returns this mutex's unique [`LockId`]. #[must_use] pub const fn id(&self) -> LockId { self.id } /// Exclusive access via `&mut` -- no locking needed. /// /// Since the caller has exclusive ownership, no other thread can /// be holding the lock. #[must_use] pub const fn get_mut(&mut self) -> &mut T { self.data.get_mut() } /// Consume the mutex and return the inner data. #[must_use] pub fn into_inner(self) -> T { self.data.into_inner() } } // -- new_higher: ordered construction with inferred level -- /// Associated function for creating mutexes ordered after existing /// ones, using the default `StdMutex` backend. /// /// For custom backends, use the [`NewHigher`] trait method instead: /// `parent.new_higher(data)`. #[cfg(feature = "std")] impl Mutex { /// Create a new mutex ordered after one or more parents. /// /// The new mutex's level is `max(parent levels) + 1`. Accepts /// a single `&Mutex` or a tuple of `&Mutex` references. /// Smart-pointer wrappers (`Arc`, `Rc`, `Box`) are also accepted. /// /// Uses the default `StdMutex` backend. For custom backends, use /// the [`NewHigher`] trait method: `parent.new_higher(data)`, /// which inherits the backend from the parent. /// /// # Examples /// /// ```rust /// use surelock::mutex::Mutex; /// /// let config: Mutex = Mutex::new(10); /// let account = Mutex::new_higher(20u32, &config); // Level<1> /// let txn = Mutex::new_higher(30u32, &account); // Level<2> /// /// // Siblings: same parent, same level /// let acct_a = Mutex::new_higher(1u32, &config); // Level<1> /// let acct_b = Mutex::new_higher(2u32, &config); // Level<1> /// /// // Multi-parent: after both config and account /// let reconciler = Mutex::new_higher(0u32, (&config, &account)); /// // Level = max(Level<0>, Level<1>) + 1 = Level<2> /// ``` #[must_use] pub fn new_higher>( data: T, parents: Parents, ) -> Mutex { parents.new_higher(data) } } // Escape hatch: direct lock bypassing the ordering system. #[cfg(feature = "escape-hatch")] impl Mutex { /// Acquire this mutex directly, bypassing the ordering system. /// /// This has the same semantics as `std::sync::Mutex::lock()`: /// no key is needed, no ordering is checked, and deadlock /// prevention is the caller's responsibility. /// /// Only available with the `escape-hatch` feature enabled. pub fn unchecked_lock(&self) -> MutexGuard<'_, R, T> { let raw_guard = self.raw.lock(); MutexGuard { data: &self.data, _raw_guard: raw_guard, } } } impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Mutex") .field("id", &self.id) .finish_non_exhaustive() } } // SAFETY: UnsafeCell: Send when T: Send. Explicit for clarity since // the Sync impl below is manual. unsafe impl Send for Mutex {} // SAFETY: UnsafeCell is unconditionally !Sync, but the RawMutex lock // guarantees exclusive access to the inner T. R: Sync because &Mutex // shares &R across threads. T: Send (not Sync) because the mutex // provides exclusive access -- we're sending T between threads, not // sharing it. unsafe impl Sync for Mutex {} // -- MutexLevel impls for Mutex references and smart pointers -- // // These feed into the blanket `MaxLevelOf` impls in `level.rs`, // which accept single parents and 2-tuples of any combination. use crate::level::MutexLevel; impl MutexLevel for &Mutex { type Lvl = Lvl; } #[cfg(target_has_atomic = "ptr")] impl MutexLevel for &alloc::sync::Arc> { type Lvl = Lvl; } impl MutexLevel for &alloc::rc::Rc> { type Lvl = Lvl; } impl MutexLevel for &alloc::boxed::Box> { type Lvl = Lvl; } // -- NewHigher impls -- // // Single mutex: child level = parent level + 1, backend inherited. // Smart pointer blankets delegate via Deref. // 2-tuple: same backend required, level = max(levels) + 1. use crate::level::NewHigher; impl NewHigher for Mutex { type NextLvl = Lvl::Next; fn new_higher(&self, data: ChildT) -> Mutex { Mutex { id: LockId::next(), raw: RawMutex::new(), data: UnsafeCell::new(data), _level: PhantomData, } } } impl + ?Sized> NewHigher for &T { type NextLvl = T::NextLvl; fn new_higher(&self, data: ChildT) -> Mutex { T::new_higher(self, data) } } #[cfg(target_has_atomic = "ptr")] impl> NewHigher for alloc::sync::Arc { type NextLvl = T::NextLvl; fn new_higher(&self, data: ChildT) -> Mutex { T::new_higher(self, data) } } impl> NewHigher for alloc::rc::Rc { type NextLvl = T::NextLvl; fn new_higher(&self, data: ChildT) -> Mutex { T::new_higher(self, data) } } impl> NewHigher for alloc::boxed::Box { type NextLvl = T::NextLvl; fn new_higher(&self, data: ChildT) -> Mutex { T::new_higher(self, data) } } // 2-tuple: both parents must share the same backend R. // Level = max(Lvl1, Lvl2) + 1. // Accepts any combination of bare refs, Arc, Rc, Box via NewHigher // bounds on each element (which give us NextLvl = parent + 1). However, we // need the RAW parent levels to compute max, so we use MutexLevel on the // reference types. // // We implement for (A, B) where A and B are references to anything with // MutexLevel, and both share backend R. This covers: // (&Mutex<_, Lvl1, R>, &Mutex<_, Lvl2, R>) // (&Arc>, &Mutex<_, Lvl2, R>) // (&Arc>, &Arc>) // etc. macro_rules! impl_new_higher_tuple { ($a_wrapper:ty, $b_wrapper:ty) => { impl< ChildT, ParentT1, ParentT2, Lvl1: IsLevel + crate::level::MaxLevel, Lvl2: IsLevel, R: RawMutex, > NewHigher for (&$a_wrapper, &$b_wrapper) where >::Max: crate::level::NextLevel, { type NextLvl = <>::Max as crate::level::NextLevel>::Next; fn new_higher(&self, data: ChildT) -> Mutex { Mutex { id: LockId::next(), raw: RawMutex::new(), data: UnsafeCell::new(data), _level: PhantomData, } } } }; } // 3x3 = 9 combinations of bare/Rc/Box (always available). impl_new_higher_tuple!(Mutex, Mutex); impl_new_higher_tuple!(Mutex, alloc::rc::Rc>); impl_new_higher_tuple!(Mutex, alloc::boxed::Box>); impl_new_higher_tuple!(alloc::rc::Rc>, Mutex); impl_new_higher_tuple!( alloc::rc::Rc>, alloc::rc::Rc> ); impl_new_higher_tuple!( alloc::rc::Rc>, alloc::boxed::Box> ); impl_new_higher_tuple!(alloc::boxed::Box>, Mutex); impl_new_higher_tuple!( alloc::boxed::Box>, alloc::rc::Rc> ); impl_new_higher_tuple!( alloc::boxed::Box>, alloc::boxed::Box> ); // +7 Arc combinations (4x4 - 3x3 = 7), requires pointer-width atomics. #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!(Mutex, alloc::sync::Arc>); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!(alloc::sync::Arc>, Mutex); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!( alloc::sync::Arc>, alloc::sync::Arc> ); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!( alloc::sync::Arc>, alloc::rc::Rc> ); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!( alloc::sync::Arc>, alloc::boxed::Box> ); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!( alloc::rc::Rc>, alloc::sync::Arc> ); #[cfg(target_has_atomic = "ptr")] impl_new_higher_tuple!( alloc::boxed::Box>, alloc::sync::Arc> );