//! 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` 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 { 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 { 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 { 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 { 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 { type Guard = >::Guard; type MinLvl = >::MinLvl; type MaxLvl = >::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`; 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; } impl<'scope, 'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> TryLockable<'scope, 'a> for Mutex { type Guard = MutexGuard<'scope, 'a, R, T>; type Lvl = Lvl; fn try_lock_impl(&'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: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::sync::Arc { type Guard = T::Guard; type Lvl = T::Lvl; fn try_lock_impl(&'a self) -> Option { T::try_lock_impl(self) } } impl<'scope, 'a, T: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::rc::Rc { type Guard = T::Guard; type Lvl = T::Lvl; fn try_lock_impl(&'a self) -> Option { T::try_lock_impl(self) } } impl<'scope, 'a, T: TryLockable<'scope, 'a>> TryLockable<'scope, 'a> for alloc::boxed::Box { type Guard = T::Guard; type Lvl = T::Lvl; fn try_lock_impl(&'a self) -> Option { 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 &'scope ()>, // *const () is !Send + !Sync. _not_send: PhantomData<*const ()>, _level: PhantomData, } impl 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 = Mutex::new(1); /// let b: Mutex> = 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, { 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 = 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 = 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, { 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 = 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, { 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 = Mutex::new(1); /// let b: Mutex = 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, >::MaxLvl>) where L: Acquirable<'scope, 'a>, >::MinLvl: LockAfter, F: FnOnce(>::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 = Mutex::new(1); /// let b: Mutex = 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, ) -> Result< ( >::Guard, MutexKey<'scope, >::MaxLvl>, ), Self, > where L: Acquirable<'scope, 'a>, >::MinLvl: LockAfter, { 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 = Mutex::new(1); /// let b: Mutex = 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, >::MaxLvl>), Self> where L: Acquirable<'scope, 'a>, >::MinLvl: LockAfter, F: FnOnce(>::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` 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` -- no `&*` deref needed for the /// smart-pointer cases. /// /// ```rust /// use surelock::{key_handle::KeyHandle, rw_lock::RwLock}; /// /// let counter: RwLock = 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, { (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 = 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, { 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` 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` -- no `&*` deref needed for the /// smart-pointer cases. /// /// ```rust /// use surelock::{key_handle::KeyHandle, rw_lock::RwLock}; /// /// let counter: RwLock = 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, { (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 = 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, { 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(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 = 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: F) -> Result 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 = 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 error pub fn lock_scope(f: F) -> Ret where 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.", ) }