parking_lot_core/
spinwait.rs

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