Something went wrong. Try again.
🦀🔐 Statically prevent deadlocks crates.io/crates/surelock
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874//! Scope token for ordered lock acquisition.//!//! [`MutexKey`] tracks the current lock level as a type parameter.//! It is consumed by [`MutexKey::lock`] and re-emitted at the new//! level. The key is `!Send + !Sync` and branded with an invariant//! lifetime to prevent escape from the [`lock_scope`] closure.
use core::marker::PhantomData;
use crate::{ acquirable::Acquirable, level::{IsLevel, LockAfter}, mutex::{Mutex, guard::MutexGuard, raw::RawMutex}, set::{LockSet, build_sorted},};
#[cfg(feature = "std")]use crate::level::Bottom;
// ── Lockable trait ──────────────────────────────────────────────
/// A target that can be locked by [`MutexKey::lock`].////// Implemented for single [`Mutex`] references and [`LockSet`]s./// Users do not need to implement this trait.#[diagnostic::on_unimplemented( message = "`{Self}` cannot be used as a lock target", note = "use `&Mutex<T>` for a single lock or `&LockSet` for multiple locks")]pub trait Lockable<'scope, 'a> { /// The guard type returned when the lock(s) are held. type Guard;
/// The minimum level in this target. type MinLvl: IsLevel;
/// The maximum level in this target. The key advances to this /// level after acquisition. type MaxLvl: IsLevel;
/// Acquire the lock(s) and return the guard(s). fn lock_impl(&'a self) -> Self::Guard;}
impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Lockable<'scope, 'a> for Mutex<T, Lvl, R> { type Guard = MutexGuard<'scope, 'a, R, T>; type MinLvl = Lvl; type MaxLvl = Lvl;
fn lock_impl(&'a self) -> Self::Guard { let raw_guard = self.raw.lock(); MutexGuard { data: &self.data, _raw_guard: raw_guard, _scope: PhantomData, } }}
#[cfg(target_has_atomic = "ptr")]impl<'scope, 'a, T: Lockable<'scope, 'a>> Lockable<'scope, 'a> for alloc::sync::Arc<T> { type Guard = T::Guard; type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard { T::lock_impl(self) }}
impl<'scope, 'a, T: Lockable<'scope, 'a>> Lockable<'scope, 'a> for alloc::rc::Rc<T> { type Guard = T::Guard; type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard { T::lock_impl(self) }}
impl<'scope, 'a, T: Lockable<'scope, 'a>> Lockable<'scope, 'a> for alloc::boxed::Box<T> { type Guard = T::Guard; type MinLvl = T::MinLvl; type MaxLvl = T::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard { T::lock_impl(self) }}
impl<'scope, 'a, L: Acquirable<'scope, 'a>> Lockable<'scope, 'a> for LockSet<L> { type Guard = <L as Acquirable<'scope, 'a>>::Guard; type MinLvl = <L as Acquirable<'scope, 'a>>::MinLvl; type MaxLvl = <L as Acquirable<'scope, 'a>>::MaxLvl;
fn lock_impl(&'a self) -> Self::Guard { self.lock_sorted() }}
// ── TryLockable trait ───────────────────────────────────────────
/// A single [`Mutex`] target that can be acquired *without blocking*/// by [`MutexKey::try_lock`].////// Implemented for a single [`Mutex`] reference and the smart-pointer/// wrappers (`Arc`, `Rc`, `Box`) around it. Deliberately **not**/// implemented for [`LockSet`]: a non-blocking/// acquisition of multiple locks requires all-or-nothing rollback,/// which is a separate (future) capability. For now, `try_lock`/// targets a single mutex.////// Users do not need to implement this trait.#[diagnostic::on_unimplemented( message = "`{Self}` cannot be acquired without blocking via `try_lock`", note = "`try_lock` targets a single `&Mutex<T>`; multi-lock non-blocking acquisition is not yet supported")]pub trait TryLockable<'scope, 'a> { /// The guard type returned when the lock is held. type Guard;
/// The level of the lock. The key advances to this level only on /// a successful acquisition. type Lvl: IsLevel;
/// Attempt to acquire without blocking. Returns `Some(guard)` if /// acquired, `None` if the lock is currently held elsewhere. fn try_lock_impl(&'a self) -> Option<Self::Guard>;}
impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> TryLockable<'scope, 'a> for Mutex<T, Lvl, R>{ type Guard = MutexGuard<'scope, 'a, R, T>; type Lvl = Lvl;
fn try_lock_impl(&'a self) -> Option<Self::Guard> { 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: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::sync::Arc<T> { type Guard = T::Guard; type Lvl = T::Lvl;
fn try_lock_impl(&'a self) -> Option<Self::Guard> { T::try_lock_impl(self) }}
impl<'scope, 'a, T: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::rc::Rc<T> { type Guard = T::Guard; type Lvl = T::Lvl;
fn try_lock_impl(&'a self) -> Option<Self::Guard> { T::try_lock_impl(self) }}
impl<'scope, 'a, T: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::boxed::Box<T> { type Guard = T::Guard; type Lvl = T::Lvl;
fn try_lock_impl(&'a self) -> Option<Self::Guard> { T::try_lock_impl(self) }}
/// Variant of [`Lockable`] that consumes the lockable by value.////// Implemented for [`ReadLock`](crate::rw_lock::ReadLock) and/// [`WriteLock`](crate::rw_lock::WriteLock), so that/// [`MutexKey::lock_owned`] can accept them inline without the/// temporary-lifetime trap that bites/// `key.lock(&WriteLock(&rw))`. The wrappers are zero-cost/// (`#[repr(transparent)]` newtypes around a reference), so passing/// them by value is the same as passing the inner reference.////// Users do not implement this trait directly.#[diagnostic::on_unimplemented( message = "`{Self}` cannot be locked by value", note = "use a `ReadLock(&rw)` / `WriteLock(&rw)` wrapper, or build a `LockSet` for multi-lock acquisition")]pub trait LockableByValue<'scope, 'a> { /// The guard type returned when the lock is held. type Guard;
/// The minimum level in this target. type MinLvl: IsLevel;
/// The maximum level in this target. The key advances to this /// level after acquisition. type MaxLvl: IsLevel;
/// Acquire the lock and return the guard. Consumes `self`. fn lock_by_value(self) -> Self::Guard;}
// ── MutexKey ────────────────────────────────────────────────────
/// Scope token for ordered lock acquisition.////// Tracks the current lock level as a type parameter `Lvl`. Consumed/// by [`lock`](MutexKey::lock) and re-emitted at the new level.////// - `!Send + !Sync` — cannot cross thread boundaries./// - Branded lifetime `'scope` — cannot escape the [`lock_scope`] closure./// - `!Clone + !Copy` — prevents using the same key twice.pub struct MutexKey<'scope, Lvl: IsLevel> { // Invariant in 'scope: fn(&'scope ()) -> &'scope () makes 'scope // neither covariant nor contravariant, preventing lifetime widening. _brand: PhantomData<fn(&'scope ()) -> &'scope ()>, // *const () is !Send + !Sync. _not_send: PhantomData<*const ()>, _level: PhantomData<Lvl>,}
impl<Lvl: IsLevel> core::fmt::Debug for MutexKey<'_, Lvl> { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.debug_struct("MutexKey").finish_non_exhaustive() }}
impl<'scope, Lvl: IsLevel> MutexKey<'scope, Lvl> { /// Create a new key. Only callable within the crate -- /// external users get keys from [`lock_scope`], [`try_lock_scope`], /// or [`KeyHandle::scope`](crate::key_handle::KeyHandle::scope). pub(crate) fn new_internal() -> Self { Self { _brand: PhantomData, _not_send: PhantomData, _level: PhantomData, } }
/// Lock one or more mutexes, returning the guard(s) directly. /// /// Accepts a single `&Mutex` or a `&LockSet`. Consumes the key /// and returns the guard(s) plus a new key at the target's level. /// /// For a single mutex this is zero-allocation; for a `LockSet` /// the sort was already done at construction time. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, level::Level, mutex::Mutex, set::LockSet}; /// /// let a: Mutex<u32> = Mutex::new(1); /// let b: Mutex<u32, Level<1>> = Mutex::new(2); /// /// lock_scope(|key| { /// // Single mutex /// let (mut guard, key) = key.lock(&a); /// *guard += 10; /// drop(guard); /// /// // Pre-sorted set /// let set = LockSet::new(&b); /// let (mut guard, _key) = key.lock(&set); /// *guard += 20; /// }); /// ``` pub fn lock<'a, L: Lockable<'scope, 'a>>( self, target: &'a L, ) -> (L::Guard, MutexKey<'scope, L::MaxLvl>) where L::MinLvl: LockAfter<Lvl>, { let guards = target.lock_impl(); (guards, MutexKey::new_internal()) }
/// Attempt to acquire a single [`Mutex`] *without blocking*. /// /// On success, returns `Ok((guard, key))` where the guard holds /// the lock and the key has advanced to the lock's level -- /// exactly as [`lock`](Self::lock) would. On contention (the lock /// is held elsewhere), returns `Err(key)` handing the **original** /// key back **unchanged** (still at the current level), so the /// caller can retry, take another path, or do other work. /// /// The asymmetry is encoded in the types: the `Ok` key is at /// `L::Lvl`, the `Err` key is at the original `Lvl`. This preserves /// the affine-witness discipline -- the key is consumed and exactly /// one key is returned on either branch -- while never advancing /// the level on a failed acquisition. /// /// Because the two keys have *different types*, you cannot bind one /// key out of a `match` over both arms /// (`let key = match ... { Ok((_, k)) => k, Err(k) => k }` is a type /// error). Either do the level-advanced work inside the `Ok` arm, or, /// to retry, reassign the *same-level* `Err` key and loop -- see /// _Recovering the key_ below. /// /// `try_lock` targets a single mutex; non-blocking acquisition of a /// [`LockSet`] (which would require /// all-or-nothing rollback) is not currently supported. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, mutex::Mutex}; /// /// let a: Mutex<u32> = Mutex::new(0); /// /// lock_scope(|key| { /// // Uncontended: succeeds and advances the key. /// match key.try_lock(&a) { /// Ok((mut guard, _key)) => *guard += 1, /// Err(_key) => unreachable!("uncontended"), /// } /// }); /// ``` /// /// # Recovering the key /// /// To spin until the lock is free, reassign the same-level key from /// the `Err` arm and loop. The advanced `Ok` key is only in scope /// inside the `Ok` arm, where the lock is held: /// /// ```rust /// use surelock::{key::lock_scope, mutex::Mutex}; /// /// let a: Mutex<u32> = Mutex::new(0); /// /// lock_scope(|mut key| { /// loop { /// match key.try_lock(&a) { /// Ok((mut guard, _next)) => { /// *guard += 1; /// break; /// } /// Err(same) => { /// key = same; // same level; retry /// std::hint::spin_loop(); /// } /// } /// } /// }); /// ``` /// /// # Errors /// /// Returns `Err(self)` (the original key, level unchanged) if the /// mutex is currently held and acquisition would block. #[allow(clippy::missing_errors_doc)] // documented above #[allow(clippy::result_large_err)] // the key is zero-sized (PhantomData) pub fn try_lock<'a, L: TryLockable<'scope, 'a>>( self, target: &'a L, ) -> Result<(L::Guard, MutexKey<'scope, L::Lvl>), Self> where L::Lvl: LockAfter<Lvl>, { match target.try_lock_impl() { Some(guard) => Ok((guard, MutexKey::new_internal())), None => Err(self), } }
/// Like [`lock`](Self::lock) but takes the lockable by value. /// /// Solves the temporary-lifetime trap with /// [`ReadLock`](crate::rw_lock::ReadLock) / /// [`WriteLock`](crate::rw_lock::WriteLock): you can write /// `key.lock_owned(WriteLock(&rw))` inline without a /// `let writer = WriteLock(&rw)` binding. The wrappers are /// `Copy` and zero-cost, so this has no performance impact. /// /// For the bare `Mutex`/`RwLock` case the convenience methods /// [`MutexKey::read`] / [`MutexKey::write`] are still more /// concise; reach for `lock_owned` when you need a wrapper /// inline that you don't want to name. /// /// # Examples /// /// ```rust /// use surelock::{ /// key::lock_scope, /// rw_lock::{ReadLock, RwLock, WriteLock}, /// }; /// /// let rw: RwLock<u32> = RwLock::new(0); /// /// lock_scope(|key| { /// let (mut g, _key) = key.lock_owned(WriteLock(&rw)); /// *g = 7; /// }); /// /// lock_scope(|key| { /// let (g, _key) = key.lock_owned(ReadLock(&rw)); /// assert_eq!(*g, 7); /// }); /// ``` pub fn lock_owned<'a, L>(self, target: L) -> (L::Guard, MutexKey<'scope, L::MaxLvl>) where L: LockableByValue<'scope, 'a>, L::MinLvl: LockAfter<Lvl>, { let guards = target.lock_by_value(); (guards, MutexKey::new_internal()) }
/// Lock one or more mutexes via a closure. /// /// Sorts by [`LockId`](crate::id::LockId) and acquires in one /// call. The guards live inside the closure -- no [`LockSet`] /// needed. Convenient for one-shot read-and-release patterns. /// /// For hot paths where the same locks are acquired repeatedly, /// pre-build a [`LockSet`] and use [`lock`](MutexKey::lock) /// instead (sort once, lock many). /// /// Consumes the key. Returns the closure's result plus a new key /// at the lock group's level. /// /// # Panics /// /// Panics if the group contains duplicate locks (same /// [`LockId`](crate::id::LockId) appearing more than once). /// /// # Examples /// /// ```rust /// use surelock::{key_handle::KeyHandle, mutex::Mutex}; /// /// let a: Mutex<u32> = Mutex::new(1); /// let b: Mutex<u32> = Mutex::new(2); /// /// let mut handle = KeyHandle::claim(); /// handle.scope(|key| { /// let (sum, _key) = key.lock_with(&(&a, &b), |(ga, gb)| *ga + *gb); /// assert_eq!(sum, 3); /// }); /// ``` #[allow(clippy::panic)] // Intentional: a duplicate is a programmer error pub fn lock_with<'a, L, F, Ret>( self, lockable: &'a L, f: F, ) -> (Ret, MutexKey<'scope, <L as Acquirable<'scope, 'a>>::MaxLvl>) where L: Acquirable<'scope, 'a>, <L as Acquirable<'scope, 'a>>::MinLvl: LockAfter<Lvl>, F: FnOnce(<L as Acquirable<'scope, 'a>>::Guard) -> Ret, { let (indices, duplicate) = build_sorted(lockable); if let Some(d) = duplicate { panic!( "lock_with called with duplicate locks: positions {} and {} are the same lock (LockId {})", d.first, d.second, d.id ); }
let guards = lockable.lock_sorted(&indices); let result = f(guards);
(result, MutexKey::new_internal()) }
/// Attempt to acquire all locks in a [`LockSet`] *without /// blocking*, all-or-nothing. /// /// The multi-lock counterpart of [`try_lock`](Self::try_lock). On /// success returns `Ok((guards, key))` with the key advanced to /// the set's `MaxLvl` (exactly as [`lock`](Self::lock) would). If /// **any** lock in the set is contended, no lock is left held -- /// the partially-acquired guards are released -- and `Err(key)` /// returns the **original** key unchanged. See /// [`try_lock`](Self::try_lock) for the `Result<(_, NewKey), /// OldKey>` rationale. /// /// Acquisition is attempted in `LockId`-sorted order (the same /// order [`lock`](Self::lock) uses), so concurrent `try_lock_set` /// callers cannot livelock each other into a cycle. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, mutex::Mutex, set::LockSet}; /// /// let a: Mutex<u32> = Mutex::new(1); /// let b: Mutex<u32> = Mutex::new(2); /// let set = LockSet::new((&a, &b)); /// /// lock_scope(|key| match key.try_lock_set(&set) { /// Ok(((ga, gb), _key)) => assert_eq!(*ga + *gb, 3), /// Err(_key) => unreachable!("uncontended"), /// }); /// ``` /// /// # Errors /// /// Returns `Err(self)` (original key, level unchanged, no locks /// held) if any lock in the set is currently held and acquisition /// would block. #[allow(clippy::missing_errors_doc)] // documented above #[allow(clippy::result_large_err)] // the key is zero-sized (PhantomData) pub fn try_lock_set<'a, L>( self, set: &'a LockSet<L>, ) -> Result< ( <L as Acquirable<'scope, 'a>>::Guard, MutexKey<'scope, <L as Acquirable<'scope, 'a>>::MaxLvl>, ), Self, > where L: Acquirable<'scope, 'a>, <L as Acquirable<'scope, 'a>>::MinLvl: LockAfter<Lvl>, { match set.try_lock_sorted() { Some(guards) => Ok((guards, MutexKey::new_internal())), None => Err(self), } }
/// Attempt to acquire one or more locks via a closure *without /// blocking*, all-or-nothing. /// /// The non-blocking counterpart of [`lock_with`](Self::lock_with). /// On success, runs `f` with the guards and returns `Ok((ret, /// key))` with the key advanced to the group's level. If any lock /// is contended, `f` is **not** called, no lock is left held, and /// `Err(key)` returns the original key unchanged. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, mutex::Mutex}; /// /// let a: Mutex<u32> = Mutex::new(1); /// let b: Mutex<u32> = Mutex::new(2); /// /// lock_scope(|key| match key.try_lock_with(&(&a, &b), |(ga, gb)| *ga + *gb) { /// Ok((sum, _key)) => assert_eq!(sum, 3), /// Err(_key) => unreachable!("uncontended"), /// }); /// ``` /// /// # Panics /// /// Panics if the group contains duplicate locks (same /// [`LockId`](crate::id::LockId) more than once). A duplicate is a /// programmer error, distinct from contention -- it would /// self-deadlock under blocking `lock_with`, and cannot succeed /// here either. /// /// # Errors /// /// Returns `Err(self)` (original key, level unchanged, no locks /// held, `f` not called) if any lock is currently held. #[allow(clippy::missing_errors_doc)] // documented above #[allow(clippy::result_large_err)] // the key is zero-sized (PhantomData) #[allow(clippy::panic)] // Intentional: a duplicate is a programmer error pub fn try_lock_with<'a, L, F, Ret>( self, lockable: &'a L, f: F, ) -> Result<(Ret, MutexKey<'scope, <L as Acquirable<'scope, 'a>>::MaxLvl>), Self> where L: Acquirable<'scope, 'a>, <L as Acquirable<'scope, 'a>>::MinLvl: LockAfter<Lvl>, F: FnOnce(<L as Acquirable<'scope, 'a>>::Guard) -> Ret, { let (indices, duplicate) = build_sorted(lockable); if let Some(d) = duplicate { panic!( "try_lock_with called with duplicate locks: positions {} and {} are the same lock (LockId {})", d.first, d.second, d.id ); }
match lockable.try_lock_sorted(&indices) { Some(guards) => Ok((f(guards), MutexKey::new_internal())), None => Err(self), } }
/// Acquire an [`RwLock`](crate::rw_lock::RwLock) in shared /// (read) mode. /// /// Equivalent to `self.lock(&ReadLock(rw))`, but goes through the /// [`ReadLockable`](crate::rw_lock::ReadLockable) fast path (which /// also accepts `&Arc`/`&Rc`/`&Box<RwLock>` directly and avoids the /// temporary-lifetime trap of an inline `&ReadLock(..)`). Consumes /// the key and returns the read guard plus a new key at the /// rwlock's level. /// /// # Examples /// /// Accepts a bare `&RwLock` or a reference to an /// `Arc`/`Rc`/`Box<RwLock>` -- no `&*` deref needed for the /// smart-pointer cases. /// /// ```rust /// use surelock::{key_handle::KeyHandle, rw_lock::RwLock}; /// /// let counter: RwLock<u32> = RwLock::new(7); /// /// let mut handle = KeyHandle::claim(); /// handle.scope(|key| { /// let (guard, _key) = key.read(&counter); /// assert_eq!(*guard, 7); /// }); /// ``` pub fn read<'a, L>(self, rw: L) -> (L::Guard, MutexKey<'scope, L::Lvl>) where L: crate::rw_lock::ReadLockable<'scope, 'a>, L::Lvl: LockAfter<Lvl>, { (rw.read_lock(), MutexKey::new_internal()) }
/// Attempt to acquire an [`RwLock`](crate::rw_lock::RwLock) in /// shared (read) mode *without blocking*. /// /// Non-blocking counterpart of [`read`](Self::read). On success, /// `Ok((guard, key))` with the key advanced to the rwlock's level; /// on contention (a writer holds the lock), `Err(key)` returns the /// original key unchanged. See [`try_lock`](Self::try_lock) for /// the rationale behind the `Result<(_, NewKey), OldKey>` shape. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, rw_lock::RwLock}; /// /// let counter: RwLock<u32> = RwLock::new(7); /// /// lock_scope(|key| { /// match key.try_read(&counter) { /// Ok((guard, _key)) => assert_eq!(*guard, 7), /// Err(_key) => unreachable!("uncontended"), /// } /// }); /// ``` /// /// # Errors /// /// Returns `Err(self)` (original key, level unchanged) if a writer /// currently holds the lock and acquisition would block. #[allow(clippy::missing_errors_doc)] // documented above #[allow(clippy::result_large_err)] // the key is zero-sized (PhantomData) pub fn try_read<'a, L>(self, rw: L) -> Result<(L::Guard, MutexKey<'scope, L::Lvl>), Self> where L: crate::rw_lock::ReadLockable<'scope, 'a>, L::Lvl: LockAfter<Lvl>, { match rw.try_read_lock() { Some(guard) => Ok((guard, MutexKey::new_internal())), None => Err(self), } }
/// Acquire an [`RwLock`](crate::rw_lock::RwLock) in exclusive /// (write) mode. /// /// Equivalent to `self.lock(&WriteLock(rw))`, but goes through the /// [`WriteLockable`](crate::rw_lock::WriteLockable) fast path (which /// also accepts `&Arc`/`&Rc`/`&Box<RwLock>` directly and avoids the /// temporary-lifetime trap of an inline `&WriteLock(..)`). Consumes /// the key and returns the write guard plus a new key at the /// rwlock's level. /// /// # Examples /// /// Accepts a bare `&RwLock` or a reference to an /// `Arc`/`Rc`/`Box<RwLock>` -- no `&*` deref needed for the /// smart-pointer cases. /// /// ```rust /// use surelock::{key_handle::KeyHandle, rw_lock::RwLock}; /// /// let counter: RwLock<u32> = RwLock::new(0); /// /// let mut handle = KeyHandle::claim(); /// handle.scope(|key| { /// let (mut guard, _key) = key.write(&counter); /// *guard = 42; /// }); /// ``` pub fn write<'a, L>(self, rw: L) -> (L::Guard, MutexKey<'scope, L::Lvl>) where L: crate::rw_lock::WriteLockable<'scope, 'a>, L::Lvl: LockAfter<Lvl>, { (rw.write_lock(), MutexKey::new_internal()) }
/// Attempt to acquire an [`RwLock`](crate::rw_lock::RwLock) in /// exclusive (write) mode *without blocking*. /// /// Non-blocking counterpart of [`write`](Self::write). On success, /// `Ok((guard, key))` with the key advanced to the rwlock's level; /// on contention (any reader or writer holds the lock), `Err(key)` /// returns the original key unchanged. See /// [`try_lock`](Self::try_lock) for the rationale behind the /// `Result<(_, NewKey), OldKey>` shape. /// /// # Examples /// /// ```rust /// use surelock::{key::lock_scope, rw_lock::RwLock}; /// /// let counter: RwLock<u32> = RwLock::new(0); /// /// lock_scope(|key| { /// match key.try_write(&counter) { /// Ok((mut guard, _key)) => *guard = 42, /// Err(_key) => unreachable!("uncontended"), /// } /// }); /// ``` /// /// # Errors /// /// Returns `Err(self)` (original key, level unchanged) if the lock /// is currently held (by a reader or writer) and acquisition would /// block. #[allow(clippy::missing_errors_doc)] // documented above #[allow(clippy::result_large_err)] // the key is zero-sized (PhantomData) pub fn try_write<'a, L>(self, rw: L) -> Result<(L::Guard, MutexKey<'scope, L::Lvl>), Self> where L: crate::rw_lock::WriteLockable<'scope, 'a>, L::Lvl: LockAfter<Lvl>, { match rw.try_write_lock() { Some(guard) => Ok((guard, MutexKey::new_internal())), None => Err(self), } }
/// Create a nested scope that inherits the current level. /// /// The inner key starts at the same level as the outer key, so /// any subsequent [`lock`](MutexKey::lock) calls must still be at /// higher levels. The outer key is consumed for the duration of /// the subscope. /// /// After the subscope returns, a key at the same level is returned /// alongside the closure's return value. The inner key gets its /// own branded lifetime, so its guards cannot escape the inner /// closure. pub fn subscope<F, Ret>(self, f: F) -> (Ret, MutexKey<'scope, Lvl>) where F: for<'inner> FnOnce(MutexKey<'inner, Lvl>) -> Ret, { let result = f(MutexKey { _brand: PhantomData, _not_send: PhantomData, _level: PhantomData, });
( result, MutexKey { _brand: PhantomData, _not_send: PhantomData, _level: PhantomData, }, ) }}
/// Try to enter an ordered lock acquisition scope.////// Only available with the `std` feature (enabled by default). On/// `no_std`, use [`KeyHandle::claim`](crate::key_handle::KeyHandle::claim)/// and [`KeyHandle::scope`](crate::key_handle::KeyHandle::scope) instead./// `scope(&mut self)` provides static nesting prevention via the/// borrow checker, without requiring `thread_local!`.////// Returns `Ok(result)` if a scope was entered successfully, or/// `Err(`[`ClaimError::AlreadyClaimed`](crate::key_handle::error::ClaimError::AlreadyClaimed)`)`/// if a scope is already active on the current thread. Use/// [`lock_scope`] for a panicking convenience wrapper.////// The `for<'scope>` bound makes `'scope` universally quantified:/// the closure must work for _any_ `'scope`, so `Ret` cannot name/// `'scope`. This prevents keys and guards from escaping the closure./// Same technique as `std::thread::scope`.////// # Errors////// Returns/// [`ClaimError::AlreadyClaimed`](crate::key_handle::error::ClaimError::AlreadyClaimed)/// if a scope is already active on this thread.////// # Examples////// ```rust/// use surelock::{key::try_lock_scope, mutex::Mutex};////// let counter: Mutex<u32> = Mutex::new(0);////// let result = try_lock_scope(|key| {/// let (mut guard, _key) = key.lock(&counter);/// *guard += 1;/// });////// match result {/// Ok(()) => { /* success */ }/// Err(_) => { /* already inside a scope */ }/// }/// ```#[cfg(feature = "std")]pub fn try_lock_scope<F, Ret>(f: F) -> Result<Ret, crate::key_handle::error::ClaimError>where F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,{ let mut handle = crate::key_handle::KeyHandle::try_claim()?; Ok(handle.scope(f))}
/// Enter an ordered lock acquisition scope, or panic if one is/// already active on this thread.////// Only available with the `std` feature (enabled by default). On/// `no_std`, use [`KeyHandle::claim`](crate::key_handle::KeyHandle::claim)/// and [`KeyHandle::scope`](crate::key_handle::KeyHandle::scope) instead.////// Convenience wrapper around [`try_lock_scope`] that panics on/// nested calls instead of returning `None`. Use this at top-level/// call sites where nesting is a programming error. Use/// [`try_lock_scope`] in library code that may be called from/// either inside or outside a scope.////// For nested locking within a scope, use [`MutexKey::subscope`].////// `lock_scope` and [`KeyHandle`](crate::key_handle::KeyHandle) share a/// single per-thread slot. If a [`KeyHandle`](crate::key_handle::KeyHandle)/// is still alive on this thread, `lock_scope` will panic -- drop the/// handle first, or use [`KeyHandle::scope`](crate::key_handle::KeyHandle::scope)/// instead of `lock_scope`. Pick one entry mechanism per thread.////// # Panics////// Panics if a scope is already active on the current thread, or if a/// [`KeyHandle`](crate::key_handle::KeyHandle) is currently held on this/// thread.////// # Examples////// ```rust/// use surelock::{key::lock_scope, mutex::Mutex};////// let counter: Mutex<u32> = Mutex::new(0);////// lock_scope(|key| {/// let (mut guard, _key) = key.lock(&counter);/// *guard += 1;/// });/// ```#[cfg(feature = "std")]#[allow(clippy::expect_used)] // Intentional -- nested lock_scope is a programming errorpub fn lock_scope<F, Ret>(f: F) -> Retwhere F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,{ try_lock_scope(f).expect( "surelock: this thread already has a KeyHandle or active scope. \ Drop it, or use key.subscope() to nest, or try_lock_scope() to recover.", )}