canic_core/ops/ic/
timer.rs

1#![allow(clippy::disallowed_methods)]
2
3pub use crate::cdk::timers::TimerId;
4
5use crate::{
6    cdk::timers::{
7        clear_timer as cdk_clear_timer, set_timer as cdk_set_timer,
8        set_timer_interval as cdk_set_timer_interval,
9    },
10    model::metrics::{
11        system::{SystemMetricKind, SystemMetrics},
12        timer::{TimerMetrics, TimerMode},
13    },
14    ops::perf::PerfOps,
15    perf::perf_counter,
16};
17use std::{cell::RefCell, future::Future, rc::Rc, time::Duration};
18
19///
20/// TimerOps
21///
22
23pub struct TimerOps;
24
25impl TimerOps {
26    /// Schedules a one-shot timer.
27    /// The task is a single Future, consumed exactly once.
28    pub fn set(
29        delay: Duration,
30        label: impl Into<String>,
31        task: impl Future<Output = ()> + 'static,
32    ) -> TimerId {
33        let label = label.into();
34
35        SystemMetrics::increment(SystemMetricKind::TimerScheduled);
36        TimerMetrics::ensure(TimerMode::Once, delay, &label);
37
38        cdk_set_timer(delay, async move {
39            TimerMetrics::increment(TimerMode::Once, delay, &label);
40
41            let start = perf_counter();
42            task.await;
43            let end = perf_counter();
44
45            PerfOps::record(&label, end.saturating_sub(start));
46        })
47    }
48
49    /// Schedules a repeating timer.
50    /// The task is a closure that produces a fresh Future on each tick.
51    pub fn set_interval<F, Fut>(interval: Duration, label: impl Into<String>, task: F) -> TimerId
52    where
53        F: FnMut() -> Fut + 'static,
54        Fut: Future<Output = ()> + 'static,
55    {
56        let label = label.into();
57
58        SystemMetrics::increment(SystemMetricKind::TimerScheduled);
59        TimerMetrics::ensure(TimerMode::Interval, interval, &label);
60
61        let task = Rc::new(RefCell::new(task));
62
63        cdk_set_timer_interval(interval, move || {
64            let label = label.clone();
65            let interval = interval;
66            let task = Rc::clone(&task);
67
68            async move {
69                TimerMetrics::increment(TimerMode::Interval, interval, &label);
70
71                let start = perf_counter();
72                let fut = { (task.borrow_mut())() };
73                fut.await;
74                let end = perf_counter();
75
76                PerfOps::record(&label, end.saturating_sub(start));
77            }
78        })
79    }
80
81    /// Clears a previously scheduled timer.
82    pub fn clear(id: TimerId) {
83        cdk_clear_timer(id);
84    }
85}