diff --git a/examples/barging.rs b/examples/barging.rs index 517c82d..247a68d 100644 --- a/examples/barging.rs +++ b/examples/barging.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use std::thread; // Requires that the `barging` feature is enabled. -use mcslock::barging::spins::Mutex; +use mcslock::barging::spins::backoff::Mutex; fn main() { const N: usize = 10; diff --git a/src/barging/mod.rs b/src/barging/mod.rs index 885fd0f..8d8b847 100644 --- a/src/barging/mod.rs +++ b/src/barging/mod.rs @@ -13,11 +13,16 @@ //! [`lock`] and [`try_lock`]. Guards are also accessible as the closure argument //! for [`lock_with`] and [`try_lock_with`] methods. //! -//! The Mutex is generic over the relax strategy. User may choose a strategy -//! as long as it implements the [`Relax`] trait. There is a number of strategies -//! provided by the [`relax`] module. Each submodule provides type aliases for -//! [`Mutex`] and [`MutexGuard`] associated with one relax strategy. See their -//! documentation for more information. +//! This Mutex is generic over the two layers of relax strategies. User may +//! choose a strategy as long as it implements the [`Relax`] trait. The shared +//! lock relax strategy is associated with the `Rs` generic paramater. The +//! handoff relax strategy is then associated with the `Rq` generic parameter. +//! Backoff relax strategies are usually prefered for shared lock contention, +//! while non-backoff relax strategies are usually prefered for handoffs. +//! +//! There is a number of strategies provided by the [`relax`] module. Each +//! submodule provides type aliases for [`Mutex`] and [`MutexGuard`] associated +//! with one relax strategy. See their documentation for more information. //! //! [lock_api]: https://crates.io/crates/lock_api //! [`lock`]: Mutex::lock @@ -47,17 +52,17 @@ pub mod spins { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Spin`] relax strategy. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Spin>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Spin, Spin>; /// A `barging` MCS lock alias that, during lock contention, will perform /// exponential backoff while signaling the processor that it is running a /// busy-wait spin-loop. pub mod backoff { use super::mutex; - use crate::relax::SpinBackoff; + use crate::relax::{Spin, SpinBackoff}; /// A `barging` MCS lock that implements the [`SpinBackoff`] relax /// strategy. @@ -71,11 +76,11 @@ pub mod spins { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`SpinBackoff`] relax /// strategy. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, SpinBackoff>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, SpinBackoff, Spin>; } } @@ -98,10 +103,10 @@ pub mod yields { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Yield`] relax strategy. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Yield>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Yield, Yield>; /// A `barging` MCS lock alias that, during lock contention, will perform /// exponential backoff while spinning up to a threshold, then yields back @@ -109,7 +114,7 @@ pub mod yields { #[cfg(feature = "yield")] pub mod backoff { use super::mutex; - use crate::relax::YieldBackoff; + use crate::relax::{Yield, YieldBackoff}; /// A `barging` MCS lock that implements the [`YieldBackoff`] relax /// strategy. @@ -123,11 +128,11 @@ pub mod yields { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`YieldBackoff`] relax /// strategy. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, YieldBackoff>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, YieldBackoff, Yield>; } } @@ -148,8 +153,8 @@ pub mod loops { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Loop`] relax strategy. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Loop>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Loop, Loop>; } diff --git a/src/barging/mutex.rs b/src/barging/mutex.rs index c8a0867..cb56a37 100644 --- a/src/barging/mutex.rs +++ b/src/barging/mutex.rs @@ -1,4 +1,5 @@ use core::fmt; +use core::marker::PhantomData; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; use crate::cfg::atomic::AtomicBool; @@ -23,9 +24,9 @@ use crate::relax::Relax; /// use std::sync::mpsc::channel; /// /// use mcslock::barging::Mutex; -/// use mcslock::relax::Spin; +/// use mcslock::relax::{Spin, SpinBackoff}; /// -/// type SpinMutex = Mutex; +/// type SpinMutex = Mutex; /// /// const N: usize = 10; /// @@ -60,26 +61,27 @@ use crate::relax::Relax; /// [`new`]: Mutex::new /// [`lock`]: Mutex::lock /// [`try_lock`]: Mutex::try_lock -pub struct Mutex { +pub struct Mutex { locked: AtomicBool, - raw: RawMutex<(), R>, + marker: PhantomData, + raw: RawMutex<(), Rq>, data: UnsafeCell, } // Same unsafe impls as `crate::raw::Mutex`. -unsafe impl Send for Mutex {} -unsafe impl Sync for Mutex {} +unsafe impl Send for Mutex {} +unsafe impl Sync for Mutex {} -impl Mutex { +impl Mutex { /// Creates a new mutex in an unlocked state ready for use. /// /// # Examples /// /// ``` /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex; /// /// const MUTEX: SpinMutex = SpinMutex::new(0); /// let mutex = SpinMutex::new(0); @@ -90,7 +92,7 @@ impl Mutex { let locked = AtomicBool::new(false); let raw = RawMutex::new(()); let data = UnsafeCell::new(value); - Self { locked, raw, data } + Self { locked, raw, data, marker: PhantomData } } /// Creates a new unlocked mutex with Loom primitives (non-const). @@ -100,7 +102,7 @@ impl Mutex { let locked = AtomicBool::new(false); let raw = RawMutex::new(()); let data = UnsafeCell::new(value); - Self { locked, raw, data } + Self { locked, raw, data, marker: PhantomData } } /// Consumes this mutex, returning the underlying data. @@ -109,9 +111,9 @@ impl Mutex { /// /// ``` /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex; /// /// let mutex = SpinMutex::new(0); /// assert_eq!(mutex.into_inner(), 0); @@ -122,7 +124,7 @@ impl Mutex { } } -impl Mutex { +impl Mutex { /// Acquires this mutex, blocking the current thread until it is able to do so. /// /// This function will block the local thread until it is available to acquire @@ -139,9 +141,9 @@ impl Mutex { /// use std::thread; /// /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex; /// /// let mutex = Arc::new(SpinMutex::new(0)); /// let c_mutex = Arc::clone(&mutex); @@ -154,14 +156,14 @@ impl Mutex { /// assert_eq!(*mutex.lock(), 10); /// ``` #[inline] - pub fn lock(&self) -> MutexGuard<'_, T, R> { + pub fn lock(&self) -> MutexGuard<'_, T, Rs, Rq> { if self.try_lock_fast() { return MutexGuard::new(self); } let mut node = MutexNode::new(); let guard = self.raw.lock(&mut node); while !self.try_lock_fast() { - let mut relax = R::new(); + let mut relax = Rs::new(); while self.locked.load(Relaxed) { relax.relax(); } @@ -186,9 +188,9 @@ impl Mutex { /// use std::thread; /// /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex::; /// /// let mutex = Arc::new(SpinMutex::new(0)); /// let c_mutex = Arc::clone(&mutex); @@ -213,13 +215,13 @@ impl Mutex { #[inline] pub fn lock_with(&self, f: F) -> Ret where - F: FnOnce(MutexGuard<'_, T, R>) -> Ret, + F: FnOnce(MutexGuard<'_, T, Rs, Rq>) -> Ret, { f(self.lock()) } } -impl Mutex { +impl Mutex { /// Attempts to acquire this mutex without blocking the thread. /// /// If the lock could not be acquired at this time, then [`None`] is returned. @@ -235,9 +237,9 @@ impl Mutex { /// use std::thread; /// /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex::; /// /// let mutex = Arc::new(SpinMutex::new(0)); /// let c_mutex = Arc::clone(&mutex); @@ -255,7 +257,7 @@ impl Mutex { /// assert_eq!(*mutex.lock(), 10); /// ``` #[inline] - pub fn try_lock(&self) -> Option> { + pub fn try_lock(&self) -> Option> { self.locked .compare_exchange(false, true, Acquire, Relaxed) .map(|_| MutexGuard::new(self)) @@ -278,9 +280,9 @@ impl Mutex { /// use std::thread; /// /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex::; /// /// let mutex = Arc::new(SpinMutex::new(0)); /// let c_mutex = Arc::clone(&mutex); @@ -311,7 +313,7 @@ impl Mutex { #[inline] pub fn try_lock_with(&self, f: F) -> Ret where - F: FnOnce(Option>) -> Ret, + F: FnOnce(Option>) -> Ret, { f(self.try_lock()) } @@ -325,9 +327,9 @@ impl Mutex { /// /// ``` /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex; /// /// let mutex = SpinMutex::new(0); /// let guard = mutex.lock(); @@ -350,9 +352,9 @@ impl Mutex { /// /// ``` /// use mcslock::barging::Mutex; - /// use mcslock::relax::Spin; + /// use mcslock::relax::{Spin, SpinBackoff}; /// - /// type SpinMutex = Mutex; + /// type SpinMutex = Mutex; /// /// let mut mutex = SpinMutex::new(0); /// *mutex.get_mut() = 10; @@ -377,7 +379,7 @@ impl Mutex { } } -impl Default for Mutex { +impl Default for Mutex { /// Creates a `Mutex`, with the `Default` value for `T`. #[inline] fn default() -> Self { @@ -385,7 +387,7 @@ impl Default for Mutex { } } -impl From for Mutex { +impl From for Mutex { /// Creates a `Mutex` from a instance of `T`. #[inline] fn from(data: T) -> Self { @@ -393,7 +395,7 @@ impl From for Mutex { } } -impl fmt::Debug for Mutex { +impl fmt::Debug for Mutex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut d = f.debug_struct("Mutex"); match self.try_lock() { @@ -405,7 +407,7 @@ impl fmt::Debug for Mutex { } #[cfg(test)] -impl crate::test::LockNew for Mutex { +impl crate::test::LockNew for Mutex { type Target = T; fn new(value: Self::Target) -> Self @@ -417,22 +419,22 @@ impl crate::test::LockNew for Mutex { } #[cfg(test)] -impl crate::test::LockWith for Mutex { - type Guard<'a> = MutexGuard<'a, Self::Target, R> +impl crate::test::LockWith for Mutex { + type Guard<'a> = MutexGuard<'a, Self::Target, Rs, Rq> where Self: 'a, Self::Target: 'a; fn try_lock_with(&self, f: F) -> Ret where - F: FnOnce(Option>) -> Ret, + F: FnOnce(Option>) -> Ret, { self.try_lock_with(f) } fn lock_with(&self, f: F) -> Ret where - F: FnOnce(MutexGuard<'_, T, R>) -> Ret, + F: FnOnce(MutexGuard<'_, T, Rs, Rq>) -> Ret, { self.lock_with(f) } @@ -443,7 +445,7 @@ impl crate::test::LockWith for Mutex { } #[cfg(all(not(loom), test))] -impl crate::test::LockData for Mutex { +impl crate::test::LockData for Mutex { fn into_inner(self) -> Self::Target where Self::Target: Sized, @@ -457,11 +459,12 @@ impl crate::test::LockData for Mutex { } #[cfg(all(feature = "lock_api", not(loom)))] -unsafe impl lock_api::RawMutex for Mutex<(), R> { +unsafe impl lock_api::RawMutex for Mutex<(), Rs, Rq> { type GuardMarker = lock_api::GuardSend; - // It is fine to const initialize `Mutex<(), R>` since the data is not going - // to be shared. And since it is a `Unit` type, copies will be optimized away. + // It is fine to const initialize `Mutex<(), Rs, Rq>` since the data is not + // going to be shared. And since it is a `Unit` type, copies will be + // optimized away. #[allow(clippy::declare_interior_mutable_const)] const INIT: Self = Self::new(()); @@ -503,17 +506,17 @@ unsafe impl lock_api::RawMutex for Mutex<(), R> { /// [`lock_with`]: Mutex::lock_with /// [`try_lock_with`]: Mutex::try_lock_with #[must_use = "if unused the Mutex will immediately unlock"] -pub struct MutexGuard<'a, T: ?Sized, R> { - lock: &'a Mutex, +pub struct MutexGuard<'a, T: ?Sized, Rs, Rq> { + lock: &'a Mutex, } // Same unsafe impls as `crate::raw::MutexGuard`. -unsafe impl Send for MutexGuard<'_, T, R> {} -unsafe impl Sync for MutexGuard<'_, T, R> {} +unsafe impl Send for MutexGuard<'_, T, Rs, Rq> {} +unsafe impl Sync for MutexGuard<'_, T, Rs, Rq> {} -impl<'a, T: ?Sized, R> MutexGuard<'a, T, R> { +impl<'a, T: ?Sized, Rs, Rq> MutexGuard<'a, T, Rs, Rq> { /// Creates a new `MutexGuard` instance. - const fn new(lock: &'a Mutex) -> Self { + const fn new(lock: &'a Mutex) -> Self { Self { lock } } @@ -527,27 +530,27 @@ impl<'a, T: ?Sized, R> MutexGuard<'a, T, R> { } } -impl Drop for MutexGuard<'_, T, R> { +impl Drop for MutexGuard<'_, T, Rs, Rq> { #[inline(always)] fn drop(&mut self) { self.lock.unlock(); } } -impl<'a, T: ?Sized + fmt::Debug, R> fmt::Debug for MutexGuard<'a, T, R> { +impl<'a, T: ?Sized + fmt::Debug, Rs, Rq> fmt::Debug for MutexGuard<'a, T, Rs, Rq> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.with(|data| fmt::Debug::fmt(data, f)) } } -impl<'a, T: ?Sized + fmt::Display, R> fmt::Display for MutexGuard<'a, T, R> { +impl<'a, T: ?Sized + fmt::Display, Rs, Rq> fmt::Display for MutexGuard<'a, T, Rs, Rq> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.with(|data| fmt::Display::fmt(data, f)) } } #[cfg(not(all(loom, test)))] -impl<'a, T: ?Sized, R> core::ops::Deref for MutexGuard<'a, T, R> { +impl<'a, T: ?Sized, Rs, Rq> core::ops::Deref for MutexGuard<'a, T, Rs, Rq> { type Target = T; /// Dereferences the guard to access the underlying data. @@ -559,7 +562,7 @@ impl<'a, T: ?Sized, R> core::ops::Deref for MutexGuard<'a, T, R> { } #[cfg(not(all(loom, test)))] -impl<'a, T: ?Sized, R> core::ops::DerefMut for MutexGuard<'a, T, R> { +impl<'a, T: ?Sized, Rs, Rq> core::ops::DerefMut for MutexGuard<'a, T, Rs, Rq> { /// Mutably dereferences the guard to access the underlying data. #[inline(always)] fn deref_mut(&mut self) -> &mut T { @@ -572,7 +575,7 @@ impl<'a, T: ?Sized, R> core::ops::DerefMut for MutexGuard<'a, T, R> { /// underlying data. #[cfg(all(loom, test))] #[cfg(not(tarpaulin_include))] -unsafe impl crate::loom::Guard for MutexGuard<'_, T, R> { +unsafe impl crate::loom::Guard for MutexGuard<'_, T, Rs, Rq> { type Target = T; fn get(&self) -> &loom::cell::UnsafeCell { diff --git a/src/lock_api/mod.rs b/src/lock_api/mod.rs index b0fc3de..184d0e2 100644 --- a/src/lock_api/mod.rs +++ b/src/lock_api/mod.rs @@ -41,18 +41,18 @@ pub mod spins { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Spin`] relax strategy /// and compatible with the `lock_api` crate. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Spin>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Spin, Spin>; /// A `barging` MCS lock alias that, during lock contention, will perform /// exponential backoff while signaling the processor that it is running a /// busy-wait spin-loop. pub mod backoff { use super::mutex; - use crate::relax::SpinBackoff; + use crate::relax::{Spin, SpinBackoff}; /// A `barging` MCS lock that implements the [`SpinBackoff`] relax /// strategy and compatible with the `lock_api` crate. @@ -66,11 +66,11 @@ pub mod spins { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`SpinBackoff`] relax /// strategy and compatible with the `lock_api` crate. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, SpinBackoff>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, SpinBackoff, Spin>; } } @@ -94,11 +94,11 @@ pub mod yields { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Yield`] relax strategy /// and compatible with the `lock_api` crate. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Yield>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Yield, Yield>; /// A `barging` MCS lock alias that, during lock contention, will perform /// exponential backoff while spinning up to a threshold, then yields back to @@ -106,7 +106,7 @@ pub mod yields { #[cfg(feature = "yield")] pub mod backoff { use super::mutex; - use crate::relax::YieldBackoff; + use crate::relax::{Yield, YieldBackoff}; /// A `barging` MCS lock that implements the [`YieldBackoff`] relax /// strategy and compatible with the `lock_api` crate. @@ -120,11 +120,11 @@ pub mod yields { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`YieldBackoff`] relax /// strategy and compatible with the `lock_api` crate. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, YieldBackoff>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, YieldBackoff, Yield>; } } @@ -146,9 +146,9 @@ pub mod loops { /// let guard = mutex.lock(); /// assert_eq!(*guard, 0); /// ``` - pub type Mutex = mutex::Mutex; + pub type Mutex = mutex::Mutex; /// A `barging` MCS guard that implements the [`Loop`] relax strategy /// and compatible with the `lock_api` crate. - pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Loop>; + pub type MutexGuard<'a, T> = mutex::MutexGuard<'a, T, Loop, Loop>; } diff --git a/src/lock_api/mutex.rs b/src/lock_api/mutex.rs index 7158291..bdb0271 100644 --- a/src/lock_api/mutex.rs +++ b/src/lock_api/mutex.rs @@ -7,14 +7,14 @@ use crate::test::{LockData, LockNew, LockWith}; /// A lock that provides mutually exclusive data access that is compatible with /// [`lock_api`](https://crates.io/crates/lock_api). -pub type Mutex = lock_api::Mutex, T>; +pub type Mutex = lock_api::Mutex, T>; /// A guard that provides mutable data access that is compatible with /// [`lock_api`](https://crates.io/crates/lock_api). -pub type MutexGuard<'a, T, R> = lock_api::MutexGuard<'a, barging::Mutex<(), R>, T>; +pub type MutexGuard<'a, T, Rs, Rq> = lock_api::MutexGuard<'a, barging::Mutex<(), Rs, Rq>, T>; #[cfg(test)] -impl LockNew for Mutex { +impl LockNew for Mutex { type Target = T; fn new(value: Self::Target) -> Self @@ -26,22 +26,22 @@ impl LockNew for Mutex { } #[cfg(test)] -impl LockWith for Mutex { - type Guard<'a> = MutexGuard<'a, Self::Target, R> +impl LockWith for Mutex { + type Guard<'a> = MutexGuard<'a, Self::Target, Rs, Rq> where Self: 'a, Self::Target: 'a; fn try_lock_with(&self, f: F) -> Ret where - F: FnOnce(Option>) -> Ret, + F: FnOnce(Option>) -> Ret, { f(self.try_lock()) } fn lock_with(&self, f: F) -> Ret where - F: FnOnce(MutexGuard<'_, T, R>) -> Ret, + F: FnOnce(MutexGuard<'_, T, Rs, Rq>) -> Ret, { f(self.lock()) } @@ -52,7 +52,7 @@ impl LockWith for Mutex { } #[cfg(test)] -impl LockData for Mutex { +impl LockData for Mutex { fn into_inner(self) -> Self::Target where Self::Target: Sized,