//! Internal trait for types that can be locked atomically. //! See [`Acquirable`]. Most users do not need to interact with this //! trait directly. pub mod tuples; use alloc::vec::Vec; use core::marker::PhantomData; use crate::{ id::LockId, level::IsLevel, mutex::{Mutex, guard::MutexGuard, raw::RawMutex}, }; /// Uniform access to a lock through any reference / wrapper type. /// /// Implemented for bare references and smart-pointer wrappers /// (`Arc`, `Rc`, `Box`) around [`Mutex`], and for the /// [`ReadLock`](crate::rw_lock::ReadLock) / /// [`WriteLock`](crate::rw_lock::WriteLock) mode wrappers around /// [`RwLock`](crate::rw_lock::RwLock) references. Used by tuple /// [`Acquirable`] impls so that /// `LockSet::new((&arc_mtx, ReadLock(&rw)))` works transparently. /// /// Associated types (`Data`, `Lvl`, `Guard`) are determined by /// `Self`, avoiding unconstrained type parameter issues in generic /// tuple impls. The `Guard` GAT lets each impl pick its own guard /// type -- exclusive [`MutexGuard`] for mutexes, shared /// [`RwLockReadGuard`](crate::rw_lock::guard::RwLockReadGuard) for /// readers, exclusive /// [`RwLockWriteGuard`](crate::rw_lock::guard::RwLockWriteGuard) for /// writers. pub trait MutexRef<'scope, 'a> { /// The data type guarded by the lock. type Data: 'a; /// The level of the lock. type Lvl: IsLevel; /// The guard type returned by [`lock_ref`](Self::lock_ref). type Guard; /// Return this lock's [`LockId`]. fn id(&self) -> LockId; /// Lock and return the guard. fn lock_ref(&'a self) -> Self::Guard; /// Attempt to lock without blocking, returning `Some(guard)` if /// acquired or `None` if the lock is currently held. Used by the /// all-or-nothing [`Acquirable::try_lock_sorted`] path. fn try_lock_ref(&'a self) -> Option; } impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> MutexRef<'scope, 'a> for &'a Mutex { type Data = T; type Lvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn id(&self) -> LockId { Mutex::id(self) } fn lock_ref(&'a self) -> MutexGuard<'scope, 'a, R, T> { MutexGuard { data: &self.data, _raw_guard: self.raw.lock(), _scope: PhantomData, } } fn try_lock_ref(&'a self) -> Option> { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } #[cfg(target_has_atomic = "ptr")] impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> MutexRef<'scope, 'a> for &'a alloc::sync::Arc> { type Data = T; type Lvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn id(&self) -> LockId { Mutex::id(self) } fn lock_ref(&'a self) -> MutexGuard<'scope, 'a, R, T> { MutexGuard { data: &self.data, _raw_guard: self.raw.lock(), _scope: PhantomData, } } fn try_lock_ref(&'a self) -> Option> { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> MutexRef<'scope, 'a> for &'a alloc::rc::Rc> { type Data = T; type Lvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn id(&self) -> LockId { Mutex::id(self) } fn lock_ref(&'a self) -> MutexGuard<'scope, 'a, R, T> { MutexGuard { data: &self.data, _raw_guard: self.raw.lock(), _scope: PhantomData, } } fn try_lock_ref(&'a self) -> Option> { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> MutexRef<'scope, 'a> for &'a alloc::boxed::Box> { type Data = T; type Lvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn id(&self) -> LockId { Mutex::id(self) } fn lock_ref(&'a self) -> MutexGuard<'scope, 'a, R, T> { MutexGuard { data: &self.data, _raw_guard: self.raw.lock(), _scope: PhantomData, } } fn try_lock_ref(&'a self) -> Option> { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } /// A type (or collection of types) that can be locked atomically. /// /// This trait is primarily internal bookkeeping -- most users /// interact with [`Mutex`], [`LockSet`](crate::set::LockSet), and /// [`MutexKey`](crate::key::MutexKey) directly. Implementations are /// provided for single `&Mutex` references and tuples of up to 12 /// mutex references. Users do not typically need to implement this /// trait. /// /// Elements can be at different levels. The `MinLvl` and `MaxLvl` /// associated types track the range. For a single mutex or /// same-level tuple, `MinLvl = MaxLvl`. /// /// The lifetime `'a` is the borrow lifetime of the lock group. Guards /// returned by [`lock_sorted`](Acquirable::lock_sorted) borrow from /// the underlying mutexes for this lifetime. /// /// This is a safe trait because a wrong implementation can only /// cause deadlocks (a liveness failure), not undefined behaviour. /// The soundness invariant is on [`RawMutex`], not on `Acquirable`. /// /// # Invariants /// /// Custom implementations must uphold these invariants: /// /// 1. **`collect_ids` and `lock_sorted` must agree on indexing.** /// `collect_ids` pushes `N` ids; `lock_sorted` receives a /// permutation of `0..N` and must lock the element at each /// index in the given order. /// 2. **`lock_sorted` must respect the permutation.** Locking in /// an order other than the one given defeats the deadlock /// prevention guarantee. /// 3. **`MinLvl` and `MaxLvl` must be accurate.** `MinLvl` is /// checked against the key's current level; `MaxLvl` becomes /// the key's new level after acquisition. Incorrect bounds /// can break cross-level ordering. #[diagnostic::on_unimplemented( message = "`{Self}` cannot be used as a lock group", note = "use `&Mutex` or tuples of mutex references to implement `Acquirable`" )] pub trait Acquirable<'scope, 'a> { /// The minimum level in this collection. /// /// Used to check that all locks are above the key's current /// level (`MinLvl: LockAfter`). type MinLvl: IsLevel; /// The maximum level in this collection. /// /// The key advances to this level after acquisition. type MaxLvl: IsLevel; /// The guard type(s) returned when all locks are held. type Guard; /// Collect the [`LockId`] of each lock into `out`. fn collect_ids(&self, out: &mut Vec); /// Acquire all locks in the order given by `sorted_indices`, /// returning the guards. /// /// `sorted_indices` must be a valid permutation of `0..N` /// (as produced by [`LockSet`](crate::set::LockSet)). Invalid /// indices will panic, not cause undefined behavior. fn lock_sorted(&'a self, sorted_indices: &[usize]) -> Self::Guard; /// Attempt to acquire all locks without blocking, in the order /// given by `sorted_indices`. Returns `Some(guards)` only if /// **every** lock was acquired; on the first contended lock, /// returns `None` and **releases any locks already taken** /// (all-or-nothing). /// /// Rollback is automatic: partially-acquired guards are held in /// local bindings that drop (releasing their locks) when this /// function returns `None`. Releasing never blocks, so partial /// release is always safe regardless of acquisition order. /// /// `sorted_indices` must be a valid permutation of `0..N`. fn try_lock_sorted(&'a self, sorted_indices: &[usize]) -> Option; } // Used by key.lock_with(&mutex, |guard| ...) impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Acquirable<'scope, 'a> for Mutex { type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn collect_ids(&self, out: &mut Vec) { out.push(self.id()); } fn lock_sorted(&'a self, _sorted_indices: &[usize]) -> Self::Guard { let raw_guard = self.raw.lock(); MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, } } fn try_lock_sorted(&'a self, _sorted_indices: &[usize]) -> Option { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } // Used by LockSet (which stores &Mutex) impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Acquirable<'scope, 'a> for &'a Mutex { type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>; fn collect_ids(&self, out: &mut Vec) { out.push(self.id()); } fn lock_sorted(&'a self, _sorted_indices: &[usize]) -> Self::Guard { let raw_guard = self.raw.lock(); MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, } } fn try_lock_sorted(&'a self, _sorted_indices: &[usize]) -> Option { self.raw.try_lock().map(|raw_guard| MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, }) } } // -- Smart pointer delegation -- // // These delegate to the inner type's Acquirable impl via Deref. // Enables key.lock_with(&arc_mutex, |g| ...) without &*. #[cfg(target_has_atomic = "ptr")] impl<'scope, 'a, T: Acquirable<'scope, 'a>> Acquirable<'scope, 'a> for alloc::sync::Arc { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard; fn collect_ids(&self, out: &mut Vec) { T::collect_ids(self, out); } fn lock_sorted(&'a self, sorted_indices: &[usize]) -> Self::Guard { T::lock_sorted(self, sorted_indices) } fn try_lock_sorted(&'a self, sorted_indices: &[usize]) -> Option { T::try_lock_sorted(self, sorted_indices) } } impl<'scope, 'a, T: Acquirable<'scope, 'a>> Acquirable<'scope, 'a> for alloc::rc::Rc { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard; fn collect_ids(&self, out: &mut Vec) { T::collect_ids(self, out); } fn lock_sorted(&'a self, sorted_indices: &[usize]) -> Self::Guard { T::lock_sorted(self, sorted_indices) } fn try_lock_sorted(&'a self, sorted_indices: &[usize]) -> Option { T::try_lock_sorted(self, sorted_indices) } } impl<'scope, 'a, T: Acquirable<'scope, 'a>> Acquirable<'scope, 'a> for alloc::boxed::Box { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard; fn collect_ids(&self, out: &mut Vec) { T::collect_ids(self, out); } fn lock_sorted(&'a self, sorted_indices: &[usize]) -> Self::Guard { T::lock_sorted(self, sorted_indices) } fn try_lock_sorted(&'a self, sorted_indices: &[usize]) -> Option { T::try_lock_sorted(self, sorted_indices) } } // Slice of mutexes -- same type, same level, dynamic length. // Guards are returned as a Vec. #[allow(clippy::indexing_slicing)] // sorted_indices are valid (produced by LockSet) impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Acquirable<'scope, 'a> for &'a [Mutex] { type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = Vec>; fn collect_ids(&self, out: &mut Vec) { for m in *self { out.push(m.id()); } } fn lock_sorted(&'a self, sorted_indices: &[usize]) -> Self::Guard { sorted_indices .iter() .map(|&i| { let raw_guard = self[i].raw.lock(); MutexGuard { data: &self[i].data, _raw_guard: raw_guard, _scope: PhantomData, } }) .collect() } fn try_lock_sorted(&'a self, sorted_indices: &[usize]) -> Option { // Acquire in sorted order; on the first contended lock, return // None. The `guards` Vec built so far is dropped on early // return, releasing every lock already taken (all-or-nothing). let mut guards: Vec> = Vec::with_capacity(sorted_indices.len()); for &i in sorted_indices { let raw_guard = self[i].raw.try_lock()?; guards.push(MutexGuard { data: &self[i].data, _raw_guard: raw_guard, _scope: PhantomData, }); } Some(guards) } }