surelock 0.1.0

Deadlock-free locks for Rust with compile time guarantees, incremental locks, and atomic lock sets.
Documentation
//! 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_mutex::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<'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<'a, T: 'a, Lvl: IsLevel, R: RawMutex + 'a> Lockable<'a> for Mutex<T, Lvl, R> {
    type Guard = MutexGuard<'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,
        }
    }
}

#[cfg(target_has_atomic = "ptr")]
impl<'a, T: Lockable<'a>> Lockable<'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<'a, T: Lockable<'a>> Lockable<'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<'a, T: Lockable<'a>> Lockable<'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<'a, L: Acquirable<'a>> Lockable<'a> for LockSet<L> {
    type Guard = <L as Acquirable<'a>>::Guard;
    type MinLvl = <L as Acquirable<'a>>::MinLvl;
    type MaxLvl = <L as Acquirable<'a>>::MaxLvl;

    fn lock_impl(&'a self) -> Self::Guard {
        self.lock_sorted()
    }
}

// ── 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<'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())
    }

    /// 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);
    /// });
    /// ```
    pub fn lock_with<'a, L, F, Ret>(
        self,
        lockable: &'a L,
        f: F,
    ) -> (Ret, MutexKey<'scope, <L as Acquirable<'a>>::MaxLvl>)
    where
        L: Acquirable<'a>,
        <L as Acquirable<'a>>::MinLvl: LockAfter<Lvl>,
        F: FnOnce(<L as Acquirable<'a>>::Guard) -> Ret,
    {
        let (indices, has_duplicates) = build_sorted(lockable);
        assert!(!has_duplicates, "lock_with called with duplicate locks");

        let guards = lockable.lock_sorted(&indices);
        let result = f(guards);

        (result, MutexKey::new_internal())
    }

    /// 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 `Some(result)` if a scope was entered successfully, or
/// `None` 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`.
///
/// # 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 {
///     Some(()) => { /* success */ }
///     None => { /* already inside a scope */ }
/// }
/// ```
#[cfg(feature = "std")]
pub fn try_lock_scope<F, Ret>(f: F) -> Option<Ret>
where
    F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,
{
    let mut handle = crate::key_handle::KeyHandle::try_claim()?;
    Some(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`].
///
/// # Panics
///
/// Panics if a scope is already active on the current 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 error
pub fn lock_scope<F, Ret>(f: F) -> Ret
where
    F: for<'scope> FnOnce(MutexKey<'scope, Bottom>) -> Ret,
{
    try_lock_scope(f).expect("nested lock_scope -- use key.subscope() or try_lock_scope()")
}