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)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)] 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