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