blob: 38128c5844d3e4316d3e753ae0fe06a424a8a94c [file] [log] [blame]
// Copyright 2016 Amanieu d'Antras
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.
#[cfg(unix)]
use libc;
#[cfg(windows)]
use winapi;
#[cfg(not(any(windows, unix)))]
use std::thread;
use std::sync::atomic::spin_loop_hint;
// Yields the rest of the current timeslice to the OS
#[cfg(windows)]
#[inline]
fn thread_yield() {
// Note that this is manually defined here rather than using the definition
// through `winapi`. The `winapi` definition comes from the `synchapi`
// header which enables the "synchronization.lib" library. It turns out,
// however that `Sleep` comes from `kernel32.dll` so this activation isn't
// necessary.
//
// This was originally identified in rust-lang/rust where on MinGW the
// libsynchronization.a library pulls in a dependency on a newer DLL not
// present in older versions of Windows. (see rust-lang/rust#49438)
//
// This is a bit of a hack for now and ideally we'd fix MinGW's own import
// libraries, but that'll probably take a lot longer than patching this here
// and avoiding the `synchapi` feature entirely.
extern "system" {
fn Sleep(a: winapi::shared::minwindef::DWORD);
}
unsafe {
// We don't use SwitchToThread here because it doesn't consider all
// threads in the system and the thread we are waiting for may not get
// selected.
Sleep(0);
}
}
#[cfg(unix)]
#[inline]
fn thread_yield() {
unsafe {
libc::sched_yield();
}
}
#[cfg(not(any(windows, unix)))]
#[inline]
fn thread_yield() {
thread::yield_now();
}
// Wastes some CPU time for the given number of iterations,
// using a hint to indicate to the CPU that we are spinning.
#[inline]
fn cpu_relax(iterations: u32) {
for _ in 0..iterations {
spin_loop_hint()
}
}
/// A counter used to perform exponential backoff in spin loops.
pub struct SpinWait {
counter: u32,
}
impl SpinWait {
/// Creates a new `SpinWait`.
#[inline]
pub fn new() -> SpinWait {
SpinWait { counter: 0 }
}
/// Resets a `SpinWait` to its initial state.
#[inline]
pub fn reset(&mut self) {
self.counter = 0;
}
/// Spins until the sleep threshold has been reached.
///
/// This function returns whether the sleep threshold has been reached, at
/// which point further spinning has diminishing returns and the thread
/// should be parked instead.
///
/// The spin strategy will initially use a CPU-bound loop but will fall back
/// to yielding the CPU to the OS after a few iterations.
#[inline]
pub fn spin(&mut self) -> bool {
if self.counter >= 20 {
return false;
}
self.counter += 1;
if self.counter <= 10 {
cpu_relax(4 << self.counter);
} else {
thread_yield();
}
true
}
/// Spins without yielding the thread to the OS.
///
/// Instead, the backoff is simply capped at a maximum value. This can be
/// used to improve throughput in `compare_exchange` loops that have high
/// contention.
#[inline]
pub fn spin_no_yield(&mut self) {
self.counter += 1;
if self.counter > 10 {
self.counter = 10;
}
cpu_relax(4 << self.counter);
}
}
impl Default for SpinWait {
#[inline]
fn default() -> SpinWait {
SpinWait::new()
}
}