diff --git a/src/hint.rs b/src/hint.rs index 0ea7455..672f500 100644 --- a/src/hint.rs +++ b/src/hint.rs @@ -1,6 +1,12 @@ //! Mocked versions of [`std::hint`] functions. /// Signals the processor that it is entering a busy-wait spin-loop. +/// +/// For loom, this is an alias of [`yield_now`] but is provided as a reflection +/// of the [`core::hint::spin_loop`] function. See the [`yield_now`] +/// documentation for more information on what effect this has. +/// +/// [`yield_now`]: crate::thread::yield_now pub fn spin_loop() { crate::sync::atomic::spin_loop_hint(); } diff --git a/src/rt/mod.rs b/src/rt/mod.rs index dbc3a0f..0e600dd 100644 --- a/src/rt/mod.rs +++ b/src/rt/mod.rs @@ -149,6 +149,95 @@ where /// /// This enables concurrent algorithms that require other threads to make /// progress. +/// +/// Using this as a hint might be necessary to reduce the number of branches +/// being investigated by loom. This might be necessary when testing spin locks, +/// since each iteration constitutes a branch point which might easily cause a +/// combinatorial explosion. +/// +/// Note that in loom, [`spin_loop`] and [`spin_loop_hint`] is an alias of this +/// function. +/// +/// [`spin_loop`]: crate::hint::spin_loop +/// [`spin_loop_hint`]: crate::sync::atomic::spin_loop_hint +/// +/// # Examples +/// +/// Testing a raw spin lock under loom. +/// +/// This is only provided as an example for when using [`spin_loop`] and +/// [`yield_now`] could be appropriate. Using a spin lock is almost always worse +/// than using a [`Mutex`] directly which spins internally before parking the +/// thread if contention is detected to save on system resources. +/// +/// [`Mutex`]: std::sync::Mutex +/// [`spin_loop_hint`]: crate::sync::atomic::spin_loop_hint +/// [`spin_loop`]: crate::hint::spin_loop +/// +/// ```no_run +/// use loom::sync::atomic::AtomicBool; +/// use loom::hint; +/// use loom::thread; +/// +/// use std::sync::Arc; +/// use std::sync::atomic::Ordering::{Acquire, Relaxed, SeqCst}; +/// +/// struct Lock { +/// locked: AtomicBool, +/// } +/// +/// impl Lock { +/// fn new() -> Self { +/// Lock { +/// locked: AtomicBool::new(false), +/// } +/// } +/// +/// fn spin(&self) { +/// while self.locked.load(Relaxed) { +/// hint::spin_loop(); +/// } +/// } +/// +/// fn lock(&self) { +/// loop { +/// self.spin(); +/// +/// if self.locked.compare_exchange(false, true, Acquire, Relaxed).is_ok() { +/// break; +/// } +/// +/// thread::yield_now(); +/// } +/// } +/// +/// fn unlock(&self) { +/// self.locked.store(false, SeqCst); +/// } +/// } +/// +/// # /* +/// #[test] +/// # */ +/// fn test_concurrent_logic() { +/// loom::model(|| { +/// let v1 = Arc::new(Lock::new()); +/// let v2 = v1.clone(); +/// +/// let t1 = thread::spawn(move || { +/// v2.lock(); +/// // critical section. +/// v2.unlock(); +/// }); +/// +/// v1.lock(); +/// // critical section. +/// v1.unlock(); +/// +/// t1.join().unwrap(); +/// }); +/// } +/// ``` pub fn yield_now() { let switch = execution(|execution| { let thread = execution.threads.active_id(); diff --git a/src/sync/atomic/mod.rs b/src/sync/atomic/mod.rs index 7ebd10c..b146d92 100644 --- a/src/sync/atomic/mod.rs +++ b/src/sync/atomic/mod.rs @@ -18,6 +18,13 @@ pub use self::ptr::AtomicPtr; pub use std::sync::atomic::Ordering; /// Signals the processor that it is entering a busy-wait spin-loop. +/// +/// For loom, this is an alias of [`yield_now`] but is provided as a reflection +/// of the deprecated [`core::sync::atomic::spin_loop_hint`] function. See the +/// [`yield_now`] documentation for more information on what effect using this +/// has on loom. +/// +/// [`yield_now`]: crate::thread::yield_now pub fn spin_loop_hint() { crate::thread::yield_now(); }