surelock 0.1.0

Deadlock-free locks for Rust with compile time guarantees, incremental locks, and atomic lock sets.
Documentation
#![allow(unsafe_code)]
//! Transferable token redeemable for a [`KeyHandle`].
//!
//! A [`KeyVoucher`] is issued by a [`Locksmith`](crate::locksmith::Locksmith)
//! and can be sent (`Send`) to another thread or core. On arrival,
//! call [`redeem`](KeyVoucher::redeem) to obtain a
//! [`KeyHandle`] for that execution context.
//!
//! `Send`, `!Clone`, `!Copy` -- each voucher is a unique, single-use token.
//!
//! # Examples
//!
//! ```rust
//! use surelock::locksmith::Locksmith;
//!
//! let smith = Locksmith::new(2).unwrap();
//! let voucher = smith.issue().unwrap();
//!
//! // Send voucher to another thread...
//! let handle = std::thread::spawn(move || {
//!     let mut key_handle = voucher.redeem().unwrap();
//!     key_handle.scope(|key| {
//!         // lock things...
//!     });
//! });
//! handle.join().unwrap();
//! ```

use crate::key_handle::KeyHandle;

/// Transferable token redeemable for a [`KeyHandle`].
///
/// Issued by [`Locksmith::issue`](crate::locksmith::Locksmith::issue).
/// `Send` (can cross thread/core boundaries), `!Clone`, `!Copy`.
///
/// On `std`, [`redeem`](KeyVoucher::redeem) checks the
/// `thread_local!` flag and returns `Option<KeyHandle>`. On `no_std`,
/// `redeem` is infallible (returns `KeyHandle` directly) -- per-context
/// uniqueness is a setup discipline.
#[allow(missing_copy_implementations)] // Intentionally !Copy -- one-use token
pub struct KeyVoucher {
    // Private field prevents external construction.
    // No PhantomData<*const ()> -- KeyVoucher IS Send.
    _private: (),
}

// Explicitly implement Send. KeyVoucher is designed to cross
// thread/core boundaries.
unsafe impl Send for KeyVoucher {}

impl KeyVoucher {
    /// Create a voucher. Only callable within the crate --
    /// external users get vouchers from
    /// [`Locksmith::issue`](crate::locksmith::Locksmith::issue).
    pub(crate) const fn new_internal() -> Self {
        Self { _private: () }
    }

    /// Redeem this voucher for a [`KeyHandle`] on the current thread.
    ///
    /// On `std`: returns `Some(KeyHandle)` if no handle is already
    /// claimed on this thread, `None` otherwise. Uses the same
    /// `thread_local!` mechanism as
    /// [`KeyHandle::try_claim`](crate::key_handle::KeyHandle::try_claim).
    ///
    /// On `no_std`: always returns `KeyHandle` (infallible). Per-context
    /// uniqueness is a setup discipline -- the
    /// [`Locksmith`](crate::locksmith::Locksmith)'s limit prevents
    /// over-issuance, but two vouchers redeemed on the same core
    /// are not detected.
    #[must_use]
    #[cfg(feature = "std")]
    pub fn redeem(self) -> Option<KeyHandle> {
        KeyHandle::try_claim()
    }

    /// Redeem this voucher for a [`KeyHandle`] on the current
    /// execution context.
    ///
    /// On `no_std`, this is infallible -- per-context uniqueness is
    /// a setup discipline.
    ///
    /// # Panics
    ///
    /// Panics if `KeyHandle::try_claim` returns `None`. This is
    /// unreachable on `no_std` (where `try_claim` always succeeds)
    /// but is retained as a defensive assertion.
    #[must_use]
    #[allow(clippy::expect_used)]
    #[cfg(not(feature = "std"))]
    pub fn redeem(self) -> KeyHandle {
        KeyHandle::try_claim().expect("KeyHandle::try_claim always succeeds on no_std")
    }
}

impl core::fmt::Debug for KeyVoucher {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_struct("KeyVoucher").finish_non_exhaustive()
    }
}