use core::{
cell::Cell,
fmt::{Debug, Formatter},
hint::spin_loop,
};
const SPIN_LIMIT: u32 = 6;
const YIELD_LIMIT: u32 = 10;
pub struct Backoff {
step: Cell<u32>,
}
impl Backoff {
#[inline]
pub const fn new() -> Self {
Backoff { step: Cell::new(0) }
}
#[inline]
pub const fn is_completed(&self) -> bool {
self.step.get() > YIELD_LIMIT
}
#[inline]
pub fn reset(&self) {
self.step.set(0);
}
#[inline]
pub fn snooze(&self) {
if self.step.get() <= SPIN_LIMIT {
let until = 1 << self.step.get();
for _ in 0..until {
spin_loop();
}
} else {
#[cfg(feature = "std")]
std::thread::yield_now();
#[cfg(not(feature = "std"))]
{
let until = 1 << self.step.get();
for _ in 0..until {
spin_loop();
}
}
}
if self.step.get() <= YIELD_LIMIT {
self.step.set(self.step.get().wrapping_add(1));
}
}
#[inline]
pub fn spin(&self) {
let until = 1 << self.step.get().min(SPIN_LIMIT);
for _ in 0..until {
spin_loop();
}
if self.step.get() <= SPIN_LIMIT {
self.step.set(self.step.get().wrapping_add(1));
}
}
}
impl Debug for Backoff {
#[inline]
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Backoff")
.field("is_completed", &self.is_completed())
.field("step", &self.step)
.finish()
}
}
impl Default for Backoff {
#[inline]
fn default() -> Backoff {
Backoff::new()
}
}