use crate::all::{Duration, Instant};
use spin_sleep::{SpinSleeper, SpinStrategy};
#[derive(Clone, Copy, Debug, Default)]
pub struct Sleeper {
sleeper: SpinSleeper,
}
impl Sleeper {
pub fn new(accuracy: u32, do_spin: bool) -> Self {
Self {
sleeper: Self::new_inner_sleeper(accuracy, do_spin),
}
}
pub fn sleep(&self, duration: Duration) {
if duration.is_positive() {
self.sleeper.sleep(duration.unsigned_abs());
}
}
pub fn accuracy(&self) -> Duration {
return Duration::nanoseconds(self.sleeper.native_accuracy_ns().into());
}
pub fn accuracy_ns(&self) -> u32 {
return self.sleeper.native_accuracy_ns();
}
fn new_inner_sleeper(accuracy: u32, do_spin: bool) -> SpinSleeper {
if do_spin {
SpinSleeper::new(accuracy).with_spin_strategy(SpinStrategy::SpinLoopHint)
} else {
SpinSleeper::new(accuracy).with_spin_strategy(SpinStrategy::YieldThread)
}
}
}
impl Sleeper {
#[inline]
pub fn calculate_accuracy(&mut self, num_samples: u32, extra_nanos: u32) {
let mut durations = vec![];
for _ in 0..num_samples {
let d = Self::sample_sleep_accuracy();
durations.push(d.whole_nanoseconds() as u32);
}
let mean_accuracy = Self::mean(durations.as_slice());
self.sleeper = Self::new_inner_sleeper(mean_accuracy.saturating_add(extra_nanos), true);
}
#[inline]
fn sample_sleep_accuracy() -> Duration {
let start = Instant::now();
std::thread::sleep(Duration::MICROSECOND.unsigned_abs());
let end = Instant::now();
end - start
}
#[inline]
fn mean(list: &[u32]) -> u32 {
let sum: u32 = Iterator::sum(list.iter());
(f64::from(sum) / (list.len() as f64)) as u32
}
#[inline]
pub fn size() -> usize {
core::mem::size_of::<Self>()
}
}