]>
Commit | Line | Data |
---|---|---|
f035d41b XL |
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 std::sync::atomic::spin_loop_hint; | |
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_hint() | |
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 | } |