mcslock 0.4.2

An implementation of Mellor-Crummey and Scott contention-free lock for mutual exclusion, referred to as MCS lock.
Documentation
use core::fmt::{self, Debug, Display, Formatter};
use core::marker::PhantomData;

use crate::cfg::cell::{UnsafeCell, UnsafeCellWith};
use crate::inner::raw;
use crate::lock::{Lock, Wait};

#[cfg(feature = "thread_local")]
mod thread_local;

/// A mutual exclusion primitive implementing a barging MCS lock protocol, useful
/// for protecting shared data.
pub struct Mutex<T: ?Sized, L, Ws, Wq> {
    lock: L,
    queue: raw::Mutex<(), L, Wq>,
    marker: PhantomData<Ws>,
    data: UnsafeCell<T>,
}

// SAFETY: A `Mutex` is safe to be sent across thread boundaries as long as
// the inlined protected data `T` is also safe to be sent to other threads.
unsafe impl<T: ?Sized + Send, L: Send, Ws, Wq> Send for Mutex<T, L, Ws, Wq> {}

// SAFETY: A `Mutex` is safe to be shared across thread boundaries since it
// guarantees linearization of access and modification to the protected data,
// but only if the protected data `T` is safe to be sent to other threads.
unsafe impl<T: ?Sized + Send, L: Sync, Ws, Wq> Sync for Mutex<T, L, Ws, Wq> {}

impl<T, L: Lock, Ws, Wq> Mutex<T, L, Ws, Wq> {
    /// Creates a new, unlocked and core based mutex (const).
    #[cfg(not(all(loom, test)))]
    pub const fn new(value: T) -> Self {
        let lock = Lock::UNLOCKED;
        let queue = raw::Mutex::new(());
        let data = UnsafeCell::new(value);
        Self { lock, queue, data, marker: PhantomData }
    }

    /// Creates a new, unlocked and loom base mutex (non-const).
    #[cfg(all(loom, test))]
    #[cfg(not(tarpaulin))]
    pub fn new(value: T) -> Self {
        let lock = Lock::unlocked();
        let queue = raw::Mutex::new(());
        let data = UnsafeCell::new(value);
        Self { lock, queue, data, marker: PhantomData }
    }
}

impl<T: ?Sized, L: Lock, Ws: Wait, Wq: Wait> Mutex<T, L, Ws, Wq> {
    /// Acquires this mutex, blocking the current thread until it is able to do so.
    ///
    /// This implementation will allocate, access and modify a queue node for
    /// each call, storing it at the current stack frame.
    #[cfg(any(test, not(feature = "thread_local")))]
    pub fn lock_with_stack_queue_node(&self) -> MutexGuard<'_, T, L, Ws, Wq> {
        self.lock(|f| {
            let mut node = raw::MutexNode::new();
            self.queue.lock_with_then(&mut node, |()| f(self));
        })
    }

    /// Generic lock implementation over call site provided queueing logic.
    ///
    /// Try to acquire this mutex, by first racing the shared lock state. On
    /// failure, enqueue the thread's node. Once it reaches the head of the
    /// queue, then try to acquire the shared lock state in a loop, while applying
    /// the user provided waiting policy (`Ws`).
    ///
    /// Node queueing must be defined through the `enqueue_then` closure.
    fn lock<F>(&self, enqueue_then: F) -> MutexGuard<'_, T, L, Ws, Wq>
    where
        F: FnOnce(fn(&Self)),
    {
        if !self.lock.try_lock_acquire_weak() {
            enqueue_then(|this| {
                while !this.lock.try_lock_acquire_weak() {
                    this.lock.wait_lock_relaxed::<Ws>();
                }
            });
        }
        MutexGuard::new(self)
    }
}

impl<T: ?Sized, L: Lock, Ws, Wq> Mutex<T, L, Ws, Wq> {
    /// Returns `true` if the lock is currently held.
    ///
    /// This function does not guarantee strong ordering, only atomicity.
    pub fn is_locked(&self) -> bool {
        self.lock.is_locked_relaxed()
    }

    /// Attempts to acquire this mutex without blocking the thread.
    ///
    /// The lock state is loaded with acquire ordering.
    pub fn try_lock(&self) -> Option<MutexGuard<'_, T, L, Ws, Wq>> {
        self.lock.try_lock_acquire().then(|| MutexGuard::new(self))
    }

    /// Unlocks this mutex.
    pub fn unlock(&self) {
        self.lock.notify_release();
    }
}

impl<T, L, Ws, Wq> Mutex<T, L, Ws, Wq> {
    /// Consumes this mutex, returning the underlying data.
    pub fn into_inner(self) -> T {
        self.data.into_inner()
    }
}

impl<T: ?Sized, L, Ws, Wq> Mutex<T, L, Ws, Wq> {
    /// Returns a mutable reference to the underlying data.
    #[cfg(not(all(loom, test)))]
    pub fn get_mut(&mut self) -> &mut T {
        // SAFETY: We hold exclusive access to the Mutex data.
        unsafe { &mut *self.data.get() }
    }
}

impl<T: ?Sized + Debug, L: Lock, Ws, Wq> Debug for Mutex<T, L, Ws, Wq> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        let mut d = f.debug_struct("Mutex");
        match self.try_lock() {
            Some(guard) => guard.with(|data| d.field("data", &data)),
            None => d.field("data", &format_args!("<locked>")),
        };
        d.finish()
    }
}

/// An RAII implementation of a "scoped lock" of a mutex. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
#[must_use = "if unused the Mutex will immediately unlock"]
pub struct MutexGuard<'a, T: ?Sized, L: Lock, Ws, Wq> {
    lock: &'a Mutex<T, L, Ws, Wq>,
}

// SAFETY: A `MutexGuard` is safe to be sent across thread boundaries as long as
// the referenced protected data `T` is also safe to be sent to other threads.
// Note that `std::sync::MutexGuard` is `!Send` because it must be compatible
// with `Pthreads` implementation on Linux, but we do not have this constraint.
unsafe impl<T: ?Sized + Send, L: Lock + Send, Ws, Wq> Send for MutexGuard<'_, T, L, Ws, Wq> {}

// SAFETY: A `MutexGuard` is safe to be shared across thread boundaries since
// it owns exclusive access over the protected data during its lifetime, and so
// the can safely share references to the data, but only if the protected data
// is also safe to be shared with other cuncurrent threads.
unsafe impl<T: ?Sized + Sync, L: Lock + Sync, Ws, Wq> Sync for MutexGuard<'_, T, L, Ws, Wq> {}

impl<'a, T: ?Sized, L: Lock, Ws, Wq> MutexGuard<'a, T, L, Ws, Wq> {
    /// Creates a new `MutexGuard` instance.
    const fn new(lock: &'a Mutex<T, L, Ws, Wq>) -> Self {
        Self { lock }
    }

    /// Runs `f` against an shared reference pointing to the underlying data.
    fn with<F, Ret>(&self, f: F) -> Ret
    where
        F: FnOnce(&T) -> Ret,
    {
        // SAFETY: A guard instance holds the lock locked.
        unsafe { self.lock.data.with_unchecked(f) }
    }
}

impl<T: ?Sized + Debug, L: Lock, Ws, Wq> Debug for MutexGuard<'_, T, L, Ws, Wq> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.with(|data| data.fmt(f))
    }
}

impl<T: ?Sized + Display, L: Lock, Ws, Wq> Display for MutexGuard<'_, T, L, Ws, Wq> {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.with(|data| data.fmt(f))
    }
}

#[cfg(not(all(loom, test)))]
impl<T: ?Sized, L: Lock, Ws, Wq> core::ops::Deref for MutexGuard<'_, T, L, Ws, Wq> {
    type Target = T;

    /// Dereferences the guard to access the underlying data.
    fn deref(&self) -> &T {
        // SAFETY: A guard instance holds the lock locked.
        unsafe { &*self.lock.data.get() }
    }
}

#[cfg(not(all(loom, test)))]
impl<T: ?Sized, L: Lock, Ws, Wq> core::ops::DerefMut for MutexGuard<'_, T, L, Ws, Wq> {
    /// Mutably dereferences the guard to access the underlying data.
    fn deref_mut(&mut self) -> &mut T {
        // SAFETY: A guard instance holds the lock locked.
        unsafe { &mut *self.lock.data.get() }
    }
}

impl<T: ?Sized, L: Lock, Ws, Wq> Drop for MutexGuard<'_, T, L, Ws, Wq> {
    fn drop(&mut self) {
        self.lock.unlock();
    }
}

// SAFETY: A guard instance hold the lock locked, with exclusive access to the
// underlying data.
#[cfg(all(loom, test))]
#[cfg(not(tarpaulin))]
unsafe impl<T: ?Sized, L: Lock, Ws, Wq> crate::loom::Guard for MutexGuard<'_, T, L, Ws, Wq> {
    type Target = T;

    fn get(&self) -> &loom::cell::UnsafeCell<Self::Target> {
        &self.lock.data
    }
}