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 ] |
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 ] |
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 | } |
75 | |