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, TimerCounters, TimerObservabilitySnapshot, TimerPerformance,
15};
16pub use model::{
17    DeclarationLifetime, InactiveReason, OrdinaryRuntimeStateSnapshot, TimerCompletion,
18    TimerCompletionOutcome, TimerControlFailure, TimerDirectiveSnapshot, TimerEpoch,
19    TimerLastOutcome, TimerOutcomeSnapshot, TimerPolicy, TimerProcessCondition,
20    TimerRegistrationStatus, TimerRunResult, TimerRuntimeStateSnapshot, TimerSchedulingMode,
21    WatchdogAttemptSnapshot, WatchdogAttemptStatus, WatchdogDecision, WatchdogRunResult,
22    WatchdogRuntimeStateSnapshot,
23};
24
25/// Canonical provider-neutral operator snapshot for one logical timer.
26#[derive(Clone, Debug, Eq, PartialEq)]
27pub struct TimerSnapshot {
28    identity: TimerIdentity,
29    policy: TimerPolicy,
30    lifetime: DeclarationLifetime,
31    state: TimerRuntimeStateSnapshot,
32    scheduling_mode: TimerSchedulingMode,
33    latest_directive: Option<TimerDirectiveSnapshot>,
34    latest_requested_delay_ns: Option<u64>,
35    latest_armed_delay_ns: Option<u64>,
36    observability: TimerObservabilitySnapshot,
37}
38
39impl TimerSnapshot {
40    #[allow(clippy::too_many_arguments)] // Registry-only constructor keeps one coherent boundary.
41    pub(crate) const fn new(
42        identity: TimerIdentity,
43        policy: TimerPolicy,
44        lifetime: DeclarationLifetime,
45        state: TimerRuntimeStateSnapshot,
46        scheduling_mode: TimerSchedulingMode,
47        latest_directive: Option<TimerDirectiveSnapshot>,
48        latest_requested_delay_ns: Option<u64>,
49        latest_armed_delay_ns: Option<u64>,
50        observability: &TimerObservabilitySnapshot,
51    ) -> Self {
52        Self {
53            identity,
54            policy,
55            lifetime,
56            state,
57            scheduling_mode,
58            latest_directive,
59            latest_requested_delay_ns,
60            latest_armed_delay_ns,
61            observability: *observability,
62        }
63    }
64
65    /// Return the stable structured identity.
66    #[must_use]
67    pub const fn identity(&self) -> &TimerIdentity {
68        &self.identity
69    }
70
71    /// Return the configured scheduling policy.
72    #[must_use]
73    pub const fn policy(&self) -> TimerPolicy {
74        self.policy
75    }
76
77    /// Return whether the declaration remains after terminal stop.
78    #[must_use]
79    pub const fn lifetime(&self) -> DeclarationLifetime {
80        self.lifetime
81    }
82
83    /// Return the closed policy-specific runtime state.
84    #[must_use]
85    pub const fn state(&self) -> TimerRuntimeStateSnapshot {
86        self.state
87    }
88
89    /// Return the effective scheduling mode.
90    ///
91    /// A new declaration starts with its configured policy mode. Later
92    /// requests and completed directives update this value, including after
93    /// the declaration becomes inactive.
94    #[must_use]
95    pub const fn scheduling_mode(&self) -> TimerSchedulingMode {
96        self.scheduling_mode
97    }
98
99    /// Return the latest completed ordinary directive.
100    #[must_use]
101    pub const fn latest_directive(&self) -> Option<TimerDirectiveSnapshot> {
102        self.latest_directive
103    }
104
105    /// Return the latest requested relative delay.
106    #[must_use]
107    pub const fn latest_requested_delay_ns(&self) -> Option<u64> {
108        self.latest_requested_delay_ns
109    }
110
111    /// Return the latest relative delay whose provider arm committed.
112    #[must_use]
113    pub const fn latest_armed_delay_ns(&self) -> Option<u64> {
114        self.latest_armed_delay_ns
115    }
116
117    /// Return the next authoritative absolute deadline.
118    #[must_use]
119    pub const fn next_deadline_ns(&self) -> Option<u64> {
120        self.state.next_deadline_ns()
121    }
122
123    /// Return a portable registration projection.
124    #[must_use]
125    pub fn registration_status(&self) -> TimerRegistrationStatus {
126        self.state.into()
127    }
128
129    /// Return an operator-facing condition derived from coherent state.
130    #[must_use]
131    pub const fn process_condition(&self) -> TimerProcessCondition {
132        match self.state {
133            TimerRuntimeStateSnapshot::Inactive {
134                reason: InactiveReason::Cancelled,
135            } => TimerProcessCondition::Disabled,
136            TimerRuntimeStateSnapshot::Inactive {
137                reason: InactiveReason::InvariantFailure | InactiveReason::ControlFailure(_),
138            } => TimerProcessCondition::Failed,
139            TimerRuntimeStateSnapshot::Inactive {
140                reason: InactiveReason::Stopped,
141            } if matches!(
142                self.observability.outcomes().last_outcome(),
143                Some(TimerLastOutcome::Completed(
144                    TimerCompletionOutcome::RetryableFailure
145                ))
146            ) =>
147            {
148                TimerProcessCondition::Failed
149            }
150            TimerRuntimeStateSnapshot::Inactive { .. } => TimerProcessCondition::Idle,
151            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_)
152                if matches!(self.scheduling_mode, TimerSchedulingMode::Retry) =>
153            {
154                TimerProcessCondition::Retrying
155            }
156            TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_) => {
157                TimerProcessCondition::Active
158            }
159        }
160    }
161
162    /// Return the latest authoritative callback generation.
163    #[must_use]
164    pub const fn generation(&self) -> Option<u64> {
165        match self.state {
166            TimerRuntimeStateSnapshot::Inactive { .. } => None,
167            TimerRuntimeStateSnapshot::Ordinary(
168                OrdinaryRuntimeStateSnapshot::Scheduled { generation, .. }
169                | OrdinaryRuntimeStateSnapshot::Running { generation },
170            ) => Some(generation),
171            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::Scheduled {
172                scheduler_generation,
173                ..
174            }) => Some(scheduler_generation),
175            TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::AwaitingWork {
176                successor_generation,
177                ..
178            }) => Some(successor_generation),
179        }
180    }
181
182    /// Return epoch-scoped outcomes, counters, and measurements.
183    #[must_use]
184    pub const fn observability(&self) -> TimerObservabilitySnapshot {
185        self.observability
186    }
187}
188
189#[cfg(test)]
190mod tests;