1mod identity;
7mod metrics;
8mod model;
9
10#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
17pub struct TimerRegistrationId {
18 epoch: TimerEpoch,
19 sequence: u64,
20}
21
22impl TimerRegistrationId {
23 pub(crate) const fn new(epoch: TimerEpoch, sequence: u64) -> Self {
24 Self { epoch, sequence }
25 }
26
27 #[must_use]
29 pub const fn epoch(self) -> TimerEpoch {
30 self.epoch
31 }
32
33 #[must_use]
35 pub const fn sequence(self) -> u64 {
36 self.sequence
37 }
38}
39
40pub use ic_metrics::MeasurementSummary;
42pub use identity::{
43 MAX_TIMER_IDENTITY_COMPONENT_BYTES, TimerIdentity, TimerIdentityError, TimerIdentityField,
44};
45pub use metrics::{
46 MemoryPageExtent, MemoryPageSample, MemoryPageSummary, TimerCounters,
47 TimerObservabilitySnapshot, TimerPerformance,
48};
49pub use model::{
50 DeclarationLifetime, InactiveReason, OrdinaryRuntimeStateSnapshot, TimerCompletion,
51 TimerCompletionOutcome, TimerControlFailure, TimerDirectiveSnapshot, TimerEpoch,
52 TimerLastOutcome, TimerOutcomeSnapshot, TimerPolicy, TimerProcessCondition,
53 TimerRegistrationStatus, TimerRuntimeStateSnapshot, TimerSchedulingMode, WatchdogAttemptStatus,
54 WatchdogDecision, WatchdogRunResult, WatchdogRuntimeStateSnapshot,
55};
56
57#[derive(Clone, Debug, Eq, PartialEq)]
63pub struct TimerInventorySnapshot {
64 epoch: TimerEpoch,
65 timers: Vec<TimerSnapshot>,
66}
67
68impl TimerInventorySnapshot {
69 pub(crate) const fn new(epoch: TimerEpoch, timers: Vec<TimerSnapshot>) -> Self {
70 Self { epoch, timers }
71 }
72
73 #[must_use]
75 pub const fn epoch(&self) -> TimerEpoch {
76 self.epoch
77 }
78
79 #[must_use]
81 pub fn timers(&self) -> &[TimerSnapshot] {
82 &self.timers
83 }
84
85 #[must_use]
87 pub fn into_timers(self) -> Vec<TimerSnapshot> {
88 self.timers
89 }
90
91 #[must_use]
93 pub const fn len(&self) -> usize {
94 self.timers.len()
95 }
96
97 #[must_use]
99 pub const fn is_empty(&self) -> bool {
100 self.timers.is_empty()
101 }
102}
103
104#[derive(Clone, Debug, Eq, PartialEq)]
106pub struct TimerSnapshot {
107 identity: TimerIdentity,
108 registration_id: TimerRegistrationId,
109 policy: TimerPolicy,
110 lifetime: DeclarationLifetime,
111 state: TimerRuntimeStateSnapshot,
112 scheduling_mode: TimerSchedulingMode,
113 latest_directive: Option<TimerDirectiveSnapshot>,
114 latest_requested_delay_ns: Option<u64>,
115 latest_armed_delay_ns: Option<u64>,
116 observability: TimerObservabilitySnapshot,
117}
118
119impl TimerSnapshot {
120 #[expect(
121 clippy::too_many_arguments,
122 reason = "Registry-only constructor keeps one coherent boundary."
123 )]
124 pub(crate) const fn new(
125 identity: TimerIdentity,
126 registration_id: TimerRegistrationId,
127 policy: TimerPolicy,
128 lifetime: DeclarationLifetime,
129 state: TimerRuntimeStateSnapshot,
130 scheduling_mode: TimerSchedulingMode,
131 latest_directive: Option<TimerDirectiveSnapshot>,
132 latest_requested_delay_ns: Option<u64>,
133 latest_armed_delay_ns: Option<u64>,
134 observability: &TimerObservabilitySnapshot,
135 ) -> Self {
136 Self {
137 identity,
138 registration_id,
139 policy,
140 lifetime,
141 state,
142 scheduling_mode,
143 latest_directive,
144 latest_requested_delay_ns,
145 latest_armed_delay_ns,
146 observability: *observability,
147 }
148 }
149
150 #[must_use]
152 pub const fn identity(&self) -> &TimerIdentity {
153 &self.identity
154 }
155
156 #[must_use]
163 pub const fn registration_id(&self) -> TimerRegistrationId {
164 self.registration_id
165 }
166
167 #[must_use]
169 pub const fn policy(&self) -> TimerPolicy {
170 self.policy
171 }
172
173 #[must_use]
175 pub const fn lifetime(&self) -> DeclarationLifetime {
176 self.lifetime
177 }
178
179 #[must_use]
181 pub const fn state(&self) -> TimerRuntimeStateSnapshot {
182 self.state
183 }
184
185 #[must_use]
191 pub const fn scheduling_mode(&self) -> TimerSchedulingMode {
192 self.scheduling_mode
193 }
194
195 #[must_use]
201 pub const fn latest_directive(&self) -> Option<TimerDirectiveSnapshot> {
202 self.latest_directive
203 }
204
205 #[must_use]
207 pub const fn latest_requested_delay_ns(&self) -> Option<u64> {
208 self.latest_requested_delay_ns
209 }
210
211 #[must_use]
213 pub const fn latest_armed_delay_ns(&self) -> Option<u64> {
214 self.latest_armed_delay_ns
215 }
216
217 #[must_use]
219 pub const fn next_deadline_ns(&self) -> Option<u64> {
220 self.state.next_deadline_ns()
221 }
222
223 #[must_use]
225 pub fn registration_status(&self) -> TimerRegistrationStatus {
226 self.state.into()
227 }
228
229 #[must_use]
231 pub const fn process_condition(&self) -> TimerProcessCondition {
232 match self.state {
233 TimerRuntimeStateSnapshot::Inactive {
234 reason: InactiveReason::Cancelled,
235 } => TimerProcessCondition::Disabled,
236 TimerRuntimeStateSnapshot::Inactive {
237 reason:
238 InactiveReason::Abandoned
239 | InactiveReason::InvariantFailure
240 | InactiveReason::ControlFailure(_),
241 } => TimerProcessCondition::Failed,
242 TimerRuntimeStateSnapshot::Inactive {
243 reason: InactiveReason::Stopped,
244 } if matches!(
245 self.observability.outcomes().last_outcome(),
246 Some(TimerLastOutcome::Completed(
247 TimerCompletionOutcome::RetryableFailure
248 ))
249 ) =>
250 {
251 TimerProcessCondition::Failed
252 }
253 TimerRuntimeStateSnapshot::Inactive { .. } => TimerProcessCondition::Idle,
254 TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_)
255 if matches!(self.scheduling_mode, TimerSchedulingMode::Retry) =>
256 {
257 TimerProcessCondition::Retrying
258 }
259 TimerRuntimeStateSnapshot::Ordinary(_) | TimerRuntimeStateSnapshot::Watchdog(_) => {
260 TimerProcessCondition::Active
261 }
262 }
263 }
264
265 #[must_use]
271 pub const fn generation(&self) -> Option<u64> {
272 match self.state {
273 TimerRuntimeStateSnapshot::Inactive { .. } => None,
274 TimerRuntimeStateSnapshot::Ordinary(
275 OrdinaryRuntimeStateSnapshot::Scheduled { generation, .. }
276 | OrdinaryRuntimeStateSnapshot::Running { generation },
277 ) => Some(generation),
278 TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::Scheduled {
279 scheduler_generation,
280 ..
281 }) => Some(scheduler_generation),
282 TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::AwaitingWork {
283 successor_generation,
284 ..
285 }) => Some(successor_generation),
286 }
287 }
288
289 #[must_use]
291 pub const fn observability(&self) -> TimerObservabilitySnapshot {
292 self.observability
293 }
294}
295
296#[cfg(test)]
297mod tests;