use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::Instant;
pub const DEFAULT_HEARTBEAT_INTERVAL_SECS: u64 = 25;
pub struct Heartbeat {
started: Instant,
last_print_nanos: AtomicU64,
interval_nanos: u64,
progress: AtomicUsize,
}
impl Heartbeat {
pub fn new(interval_secs: u64) -> Self {
Self {
started: Instant::now(),
last_print_nanos: AtomicU64::new(0),
interval_nanos: interval_secs.saturating_mul(1_000_000_000),
progress: AtomicUsize::new(0),
}
}
pub fn default_interval() -> Self {
Self::new(DEFAULT_HEARTBEAT_INTERVAL_SECS)
}
pub fn tick(&self, delta: usize, emit: impl FnOnce(usize, f64)) {
let progress = self
.progress
.fetch_add(delta, Ordering::Relaxed)
.saturating_add(delta);
let elapsed = self.started.elapsed().as_nanos() as u64;
let last = self.last_print_nanos.load(Ordering::Relaxed);
if elapsed < last.saturating_add(self.interval_nanos) {
return;
}
if self
.last_print_nanos
.compare_exchange(last, elapsed, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
emit(progress, elapsed as f64 / 1.0e9);
}
}
pub fn progress(&self) -> usize {
self.progress.load(Ordering::Relaxed)
}
pub fn elapsed_secs(&self) -> f64 {
self.started.elapsed().as_secs_f64()
}
}