gam_runtime/
loop_progress.rs1use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
8use std::time::Instant;
9
10pub const DEFAULT_LOOP_PROGRESS_INTERVAL_SECS: u64 = 25;
11
12pub struct LoopProgress {
13 started: Instant,
14 last_emit_nanos: AtomicU64,
15 interval_nanos: u64,
16 progress: AtomicUsize,
17}
18
19impl LoopProgress {
20 pub fn new(interval_secs: u64) -> Self {
21 Self {
22 started: Instant::now(),
23 last_emit_nanos: AtomicU64::new(0),
24 interval_nanos: interval_secs.saturating_mul(1_000_000_000),
25 progress: AtomicUsize::new(0),
26 }
27 }
28
29 pub fn default_interval() -> Self {
30 Self::new(DEFAULT_LOOP_PROGRESS_INTERVAL_SECS)
31 }
32
33 pub fn tick(&self, delta: usize, emit: impl FnOnce(usize, f64)) {
38 let progress = self
39 .progress
40 .fetch_add(delta, Ordering::Relaxed)
41 .saturating_add(delta);
42 let elapsed = self.started.elapsed().as_nanos() as u64;
43 let last = self.last_emit_nanos.load(Ordering::Relaxed);
44 if elapsed < last.saturating_add(self.interval_nanos) {
45 return;
46 }
47 if self
48 .last_emit_nanos
49 .compare_exchange(last, elapsed, Ordering::Relaxed, Ordering::Relaxed)
50 .is_ok()
51 {
52 emit(progress, elapsed as f64 / 1.0e9);
53 }
54 }
55}