Something went wrong. Try again.
🦀🔐 Statically prevent deadlocks crates.io/crates/surelock
typesafe utility deadlock-freedom library
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417//! 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<Self::Guard>;}
impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> MutexRef<'scope, 'a> for &'a Mutex<T, Lvl, R>{ 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<MutexGuard<'scope, 'a, R, T>> { 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<Mutex<T, Lvl, R>>{ 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<MutexGuard<'scope, 'a, R, T>> { 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<Mutex<T, Lvl, R>>{ 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<MutexGuard<'scope, 'a, R, T>> { 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<Mutex<T, Lvl, R>>{ 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<MutexGuard<'scope, 'a, R, T>> { 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<T>` 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<CurrentKeyLevel>`). 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<LockId>);
/// 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<Self::Guard>;}
// Used by key.lock_with(&mutex, |guard| ...)impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Acquirable<'scope, 'a> for Mutex<T, Lvl, R>{ type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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::Guard> { 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<T, Lvl, R>{ type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = MutexGuard<'scope, 'a, R, T>;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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::Guard> { 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<T> { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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<Self::Guard> { T::try_lock_sorted(self, sorted_indices) }}
impl<'scope, 'a, T: Acquirable<'scope, 'a>> Acquirable<'scope, 'a> for alloc::rc::Rc<T> { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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<Self::Guard> { T::try_lock_sorted(self, sorted_indices) }}
impl<'scope, 'a, T: Acquirable<'scope, 'a>> Acquirable<'scope, 'a> for alloc::boxed::Box<T> { type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl; type Guard = T::Guard;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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<Self::Guard> { 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<T, Lvl, R>]{ type MinLvl = Lvl; type MaxLvl = Lvl; type Guard = Vec<MutexGuard<'scope, 'a, R, T>>;
fn collect_ids(&self, out: &mut Vec<LockId>) { 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<Self::Guard> { // 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<MutexGuard<'scope, 'a, R, T>> = 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) }}