parking_lot_core/spinwait.rs
1use crate::thread_parker;
2use core::hint::spin_loop;
3
4// Wastes some CPU time for the given number of iterations,
5// using a hint to indicate to the CPU that we are spinning.
6#[inline]
7fn cpu_relax(iterations: u32) {
8 for _ in 0..iterations {
9 spin_loop()
10 }
11}
12
13/// A counter used to perform exponential backoff in spin loops.
14#[derive(Default)]
15pub struct SpinWait {
16 counter: u32,
17}
18
19impl SpinWait {
20 /// Creates a new `SpinWait`.
21 #[inline]
22 pub const fn new() -> Self {
23 Self { counter: 0 }
24 }
25
26 /// Resets a `SpinWait` to its initial state.
27 #[inline]
28 pub const fn reset(&mut self) {
29 self.counter = 0;
30 }
31
32 /// Spins until the sleep threshold has been reached.
33 ///
34 /// This function returns `false` if the sleep threshold has been reached,
35 /// at which point further spinning has diminishing returns and the thread
36 /// should be parked instead.
37 ///
38 /// The spin strategy will initially use a CPU-bound loop but will fall back
39 /// to yielding the CPU to the OS after a few iterations.
40 #[inline]
41 pub fn spin(&mut self) -> bool {
42 if self.counter >= 10 {
43 return false;
44 }
45 self.counter += 1;
46 if self.counter <= 3 {
47 cpu_relax(1 << (self.counter - 1));
48 } else {
49 thread_parker::thread_yield();
50 }
51 true
52 }
53
54 /// Spins without yielding the thread to the OS.
55 ///
56 /// Instead, the backoff is simply capped at a maximum value. This can be
57 /// used to improve throughput in `compare_exchange` loops that have high
58 /// contention.
59 #[inline]
60 pub fn spin_no_yield(&mut self) {
61 self.counter += 1;
62 if self.counter > 10 {
63 self.counter = 10;
64 }
65 cpu_relax(1 << (self.counter - 1));
66 }
67}