use core::cell::Cell;
#[cfg(feature = "std")]
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "std")]
use branches::likely;
pub(crate) struct Backoff {
spin: Cell<u32>,
#[cfg(feature = "std")]
snooze_fn: fn(&Self),
}
#[cfg(not(feature = "std"))]
impl Backoff {
#[inline(always)]
pub fn new() -> Self {
Self { spin: Cell::new(0) }
}
#[inline(always)]
pub fn snooze(&self) {
let spin: u32 = self.spin.get();
for _ in 0..(1 << spin.min(5)) {
core::hint::spin_loop();
}
self.spin.set(spin + 1);
}
}
#[cfg(feature = "std")]
impl Backoff {
#[inline(always)]
pub fn new() -> Self {
Self {
spin: Cell::new(0),
snooze_fn: if get_parallelism() > 1 {
Self::snooze_multi_core
} else {
Self::snooze_single_core
},
}
}
#[inline(always)]
#[cold]
pub fn snooze(&self) {
(self.snooze_fn)(self);
}
#[inline(always)]
fn snooze_multi_core(&self) {
let spin: u32 = self.spin.get();
if spin <= 6 {
for _ in 0..(1 << spin.min(5)) {
core::hint::spin_loop();
}
} else {
std::thread::yield_now();
}
self.spin.set(spin + 1);
}
#[inline(always)]
fn snooze_single_core(&self) {
std::thread::yield_now();
self.spin.set(self.spin.get() + 1);
}
#[inline(always)]
pub fn is_completed(&self) -> bool {
self.spin.get() > 32
}
}
#[inline(always)]
#[cfg(feature = "std")]
pub fn get_parallelism() -> usize {
static PARALLELISM: AtomicUsize = AtomicUsize::new(0);
let cached = PARALLELISM.load(Ordering::Relaxed);
if likely(cached != 0) {
return cached;
}
let parallelism = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1);
PARALLELISM.store(parallelism, Ordering::Release);
parallelism
}
#[cfg(not(feature = "std"))]
pub fn get_parallelism() -> usize {
1
}