• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 use crate::thread_parker;
9 use 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]
cpu_relax(iterations: u32)14 fn 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)]
22 pub struct SpinWait {
23     counter: u32,
24 }
25 
26 impl SpinWait {
27     /// Creates a new `SpinWait`.
28     #[inline]
new() -> Self29     pub fn new() -> Self {
30         Self::default()
31     }
32 
33     /// Resets a `SpinWait` to its initial state.
34     #[inline]
reset(&mut self)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]
spin(&mut self) -> bool48     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]
spin_no_yield(&mut self)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 }
75