Skip to main content

ic_timers/snapshot/
mod.rs

1//! Provider-neutral identity and coherent canonical snapshot values.
2//!
3//! Public snapshots are inert observations with private top-level fields. The
4//! registry is their only constructor and mutation authority.
5
6mod identity;
7mod metrics;
8mod model;
9
10pub use identity::{
11    MAX_TIMER_IDENTITY_COMPONENT_BYTES, TimerIdentity, TimerIdentityError, TimerIdentityField,
12};
13pub use metrics::{
14    MeasurementSummary, MemoryPageExtent, MemoryPageSample, MemoryPageSummary, TimerCounters,
15    TimerObservabilitySnapshot, TimerPerformance,
16};
17pub use model::{
18    DeclarationLifetime, InactiveReason, OrdinaryRuntimeStateSnapshot, TimerCompletion,
19    TimerCompletionOutcome, TimerControlFailure, TimerDirectiveSnapshot, TimerEpoch,
20    TimerLastOutcome, TimerOutcomeSnapshot, TimerPolicy, TimerProcessCondition,
21    TimerRegistrationStatus, TimerRunResult, TimerRuntimeStateSnapshot, TimerSchedulingMode,
22    WatchdogAttemptSnapshot, WatchdogAttemptStatus, WatchdogDecision, WatchdogRunResult,
23    WatchdogRuntimeStateSnapshot,
24};
25
26/// Atomic provider-neutral snapshot of one complete canister-local inventory.
27///
28/// The epoch remains observable even when no timer is declared. Timer order is
29/// deterministic by [`TimerIdentity`], every contained timer belongs to the
30/// returned epoch, and the bounded registry is the only constructor.
31#[derive(Clone, Debug, Eq, PartialEq)]
32pub struct TimerInventorySnapshot {
33    epoch: TimerEpoch,
34    timers: Vec<TimerSnapshot>,
35}
36
37impl TimerInventorySnapshot {
38    pub(crate) const fn new(epoch: TimerEpoch, timers: Vec<TimerSnapshot>) -> Self {
39        Self { epoch, timers }
40    }
41
42    /// Return the volatile runtime epoch shared by the complete inventory.
43    #[must_use]
44    pub const fn epoch(&self) -> TimerEpoch {
45        self.epoch
46    }
47
48    /// Return all timer snapshots in deterministic identity order.
49    #[must_use]
50    pub fn timers(&self) -> &[TimerSnapshot] {
51        &self.timers
52    }
53
54    /// Consume the inventory and return its ordered timer snapshots.
55    #[must_use]
56    pub fn into_timers(self) -> Vec<TimerSnapshot> {
57        self.timers
58    }
59
60    /// Return the number of declared logical timers.
61    #[must_use]
62    pub const fn len(&self) -> usize {
63        self.timers.len()
64    }
65
66    /// Return whether the initialized registry has no declarations.
67    #[must_use]
68    pub const fn is_empty(&self) -> bool {
69        self.timers.is_empty()
70    }
71}
72
73/// Canonical provider-neutral operator snapshot for one logical timer.
74#[derive(Clone, Debug, Eq, PartialEq)]
75pub struct TimerSnapshot {
76    identity: TimerIdentity,
77    policy: TimerPolicy,
78    lifetime: DeclarationLifetime,
79    state: TimerRuntimeStateSnapshot,
80    scheduling_mode: TimerSchedulingMode,
81    latest_directive: Option<TimerDirectiveSnapshot>,
82    latest_requested_delay_ns: Option<u64>,
83    latest_armed_delay_ns: Option<u64>,
84    observability: TimerObservabilitySnapshot,
85}
86
87impl TimerSnapshot {
88    #[allow(clippy::too_many_arguments)] // Registry-only constructor keeps one coherent boundary.
89    pub(crate) const fn new(
90        identity: TimerIdentity,
91        policy: TimerPolicy,
92        lifetime: DeclarationLifetime,
93        state: TimerRuntimeStateSnapshot,
94        scheduling_mode: TimerSchedulingMode,
95        latest_directive: Option<TimerDirectiveSnapshot>,
96        latest_requested_delay_ns: Option<u64>,
97        latest_armed_delay_ns: Option<u64>,
98        observability: &TimerObservabilitySnapshot,
99    ) -> Self {
100        Self {
101            identity,
102            policy,
103            lifetime,
104            state,
105            scheduling_mode,
106            latest_directive,
107            latest_requested_delay_ns,
108            latest_armed_delay_ns,
109            observability: *observability,
110        }
111    }
112
113    /// Return the stable structured identity.
114    #[must_use]
115    pub const fn identity(&self) -> &TimerIdentity {
116        &self.identity
117    }
118
119    /// Return the configured scheduling policy.
120    #[must_use]
121    pub const fn policy(&self) -> TimerPolicy {
122        self.policy
123    }
124
125    /// Return whether the declaration remains after terminal stop.
126    #[must_use]
127    pub const fn lifetime(&self) -> DeclarationLifetime {
128        self.lifetime
129    }
130
131    /// Return the closed policy-specific runtime state.
132    #[must_use]
133    pub const fn state(&self) -> TimerRuntimeStateSnapshot {
134        self.state
135    }
136
137    /// Return the effective scheduling mode.
138    ///
139    /// A new declaration starts with its configured policy mode. Later
140    /// requests and completed directives update this value, including after
141    /// the declaration becomes inactive.
142    #[must_use]
143    pub const fn scheduling_mode(&self) -> TimerSchedulingMode {
144        self.scheduling_mode
145    }
146
147    /// Return the latest completed ordinary directive.
148    #[must_use]
149    pub const fn latest_directive(&self) -> Option<TimerDirectiveSnapshot> {
150        self.latest_directive
151    }
152
153    /// Return the latest requested relative delay.
154    #[must_use]
155    pub const fn latest_requested_delay_ns(&self) -> Option<u64> {
156        self.latest_requested_delay_ns
157    }
158
159    /// Return the latest relative delay whose provider arm committed.
160    #[must_use]
161    pub const fn latest_armed_delay_ns(&self) -> Option<u64> {
162        self.latest_armed_delay_ns
163    }
164
165    /// Return the next authoritative absolute deadline.
166    #[must_use]
167    pub const fn next_deadline_ns(&self) -> Option<u64> {
168        self.state.next_deadline_ns()
169    }
170
171    /// Return a portable registration projection.
172    #[must_use]
173    pub fn registration_status(&self) -> TimerRegistrationStatus {
174        self.state.into()
175    }
176
177    /// Return an operator-facing condition derived from coherent state.
178    #[must_use]
179    pub const fn process_condition(&self) -> TimerProcessCondition {
180        match self.state {
181            TimerRuntimeStateSnapshot::Inactive {
182                reason: InactiveReason::Cancelled,
183            } => TimerProcessCondition::Disabled,
184            TimerRuntimeStateSnapshot::Inactive {
185                reason: InactiveReason::InvariantFailure | InactiveReason::ControlFailure(_),
186            } => TimerProcessCondition::Failed,
187            TimerRuntimeStateSnapshot::Inactive {
188                reason: InactiveReason::Stopped,
189            } if matches!(
190                self.observability.outcomes().last_outcome(),
191                Some(TimerLastOutcome::Completed(
192                    TimerCompletionOutcome::RetryableFailure
193                ))
194            ) =>
195            {
196                TimerProcessCondition::Failed
197            }
198            TimerRuntimeStateSnapshot::Inactive { .. } => TimerProcessCondition::Idle,
199            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_)
200                if matches!(self.scheduling_mode, TimerSchedulingMode::Retry) =>
201            {
202                TimerProcessCondition::Retrying
203            }
204            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_) => {
205                TimerProcessCondition::Active
206            }
207        }
208    }
209
210    /// Return the latest authoritative callback generation.
211    #[must_use]
212    pub const fn generation(&self) -> Option<u64> {
213        match self.state {
214            TimerRuntimeStateSnapshot::Inactive { .. } => None,
215            TimerRuntimeStateSnapshot::Ordinary(
216                OrdinaryRuntimeStateSnapshot::Scheduled { generation, .. }
217                | OrdinaryRuntimeStateSnapshot::Running { generation },
218            ) => Some(generation),
219            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::Scheduled {
220                scheduler_generation,
221                ..
222            }) => Some(scheduler_generation),
223            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::AwaitingWork {
224                successor_generation,
225                ..
226            }) => Some(successor_generation),
227        }
228    }
229
230    /// Return epoch-scoped outcomes, counters, and measurements.
231    #[must_use]
232    pub const fn observability(&self) -> TimerObservabilitySnapshot {
233        self.observability
234    }
235}
236
237#[cfg(test)]
238mod tests;