Skip to main content

ic_timers/
control.rs

1//! Deterministic arbitration for one logical timer identity.
2//!
3//! This module owns no task execution, platform timer handles, persistence, or
4//! time source.
5
6use thiserror::Error;
7
8/// Current registration state for one timer identity.
9#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
10pub enum TimerRegistration {
11    /// No callback is scheduled or running.
12    #[default]
13    Unregistered,
14    /// One generation is scheduled for an absolute nanosecond deadline.
15    Scheduled {
16        /// Generation owned by the scheduled callback.
17        generation: u64,
18        /// Absolute IC timestamp in nanoseconds.
19        deadline_ns: u64,
20    },
21    /// One generation currently owns logical execution.
22    Running {
23        /// Generation owned by the running callback.
24        generation: u64,
25    },
26}
27
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29enum PendingCommand {
30    Cancel { sequence: u64 },
31    Reconcile { sequence: u64, deadline_ns: u64 },
32    Schedule { sequence: u64, deadline_ns: u64 },
33}
34
35/// Side effect requested from the timer platform boundary.
36#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub enum TimerControlAction {
38    /// No platform change is required.
39    None,
40    /// Arm a new callback.
41    Arm {
42        /// Generation the callback must present when it begins.
43        generation: u64,
44        /// Absolute IC timestamp in nanoseconds.
45        deadline_ns: u64,
46    },
47    /// Clear the existing handle and arm this replacement.
48    Replace {
49        /// Generation the replacement callback must present.
50        generation: u64,
51        /// Absolute IC timestamp in nanoseconds.
52        deadline_ns: u64,
53    },
54    /// Clear the existing scheduled handle.
55    Clear,
56    /// The completed run leaves no scheduled successor.
57    Disarm {
58        /// Whether an explicit cancellation won over the run's directive.
59        cancelled: bool,
60    },
61}
62
63/// Invalid or exhausted timer-control transition.
64#[non_exhaustive]
65#[derive(Clone, Copy, Debug, Eq, Error, PartialEq)]
66pub enum TimerControlError {
67    /// The monotonic request sequence cannot be incremented.
68    #[error("timer request sequence exhausted")]
69    RequestSequenceExhausted,
70    /// The callback generation cannot be incremented.
71    #[error("timer generation exhausted")]
72    GenerationExhausted,
73    /// Completion did not present the generation that owns execution.
74    #[error("timer completion does not own the running generation")]
75    StaleCompletion,
76}
77
78/// Pure state machine for one logical timer identity.
79#[derive(Debug, Default)]
80pub struct TimerControl {
81    generation: u64,
82    request_sequence: u64,
83    registration: TimerRegistration,
84    pending: Option<PendingCommand>,
85}
86
87impl TimerControl {
88    /// Return the latest allocated callback generation.
89    #[must_use]
90    pub const fn generation(&self) -> u64 {
91        self.generation
92    }
93
94    /// Return the current logical registration.
95    #[must_use]
96    pub const fn registration(&self) -> TimerRegistration {
97        self.registration
98    }
99
100    /// Schedule a deadline, retaining an already scheduled earlier deadline.
101    pub fn schedule(&mut self, deadline_ns: u64) -> Result<TimerControlAction, TimerControlError> {
102        let sequence = self.next_request_sequence()?;
103
104        match self.registration {
105            TimerRegistration::Unregistered => {
106                let generation = self.next_generation()?;
107                self.request_sequence = sequence;
108                self.generation = generation;
109                self.registration = TimerRegistration::Scheduled {
110                    generation,
111                    deadline_ns,
112                };
113                Ok(TimerControlAction::Arm {
114                    generation,
115                    deadline_ns,
116                })
117            }
118            TimerRegistration::Scheduled {
119                deadline_ns: current_deadline,
120                ..
121            } if deadline_ns < current_deadline => {
122                let generation = self.next_generation()?;
123                self.request_sequence = sequence;
124                self.generation = generation;
125                self.registration = TimerRegistration::Scheduled {
126                    generation,
127                    deadline_ns,
128                };
129                Ok(TimerControlAction::Replace {
130                    generation,
131                    deadline_ns,
132                })
133            }
134            TimerRegistration::Scheduled { .. } => {
135                self.request_sequence = sequence;
136                Ok(TimerControlAction::None)
137            }
138            TimerRegistration::Running { .. } => {
139                self.request_sequence = sequence;
140                self.pending = Some(match self.pending {
141                    Some(
142                        PendingCommand::Schedule {
143                            deadline_ns: current_deadline,
144                            ..
145                        }
146                        | PendingCommand::Reconcile {
147                            deadline_ns: current_deadline,
148                            ..
149                        },
150                    ) => PendingCommand::Schedule {
151                        sequence,
152                        deadline_ns: current_deadline.min(deadline_ns),
153                    },
154                    Some(PendingCommand::Cancel { .. }) | None => PendingCommand::Schedule {
155                        sequence,
156                        deadline_ns,
157                    },
158                });
159                Ok(TimerControlAction::None)
160            }
161        }
162    }
163
164    /// Cancel this timer, deferring cancellation safely when a callback runs.
165    pub fn cancel(&mut self) -> Result<TimerControlAction, TimerControlError> {
166        let sequence = self.next_request_sequence()?;
167
168        match self.registration {
169            TimerRegistration::Unregistered => {
170                self.request_sequence = sequence;
171                Ok(TimerControlAction::None)
172            }
173            TimerRegistration::Scheduled { .. } => {
174                let generation = self.next_generation()?;
175                self.request_sequence = sequence;
176                self.generation = generation;
177                self.registration = TimerRegistration::Unregistered;
178                self.pending = None;
179                Ok(TimerControlAction::Clear)
180            }
181            TimerRegistration::Running { .. } => {
182                self.request_sequence = sequence;
183                self.pending = Some(PendingCommand::Cancel { sequence });
184                Ok(TimerControlAction::None)
185            }
186        }
187    }
188
189    /// Reconcile this timer to one authoritative deadline.
190    pub fn reconcile(&mut self, deadline_ns: u64) -> Result<TimerControlAction, TimerControlError> {
191        let sequence = self.next_request_sequence()?;
192
193        match self.registration {
194            TimerRegistration::Unregistered => {
195                let generation = self.next_generation()?;
196                self.request_sequence = sequence;
197                self.generation = generation;
198                self.registration = TimerRegistration::Scheduled {
199                    generation,
200                    deadline_ns,
201                };
202                Ok(TimerControlAction::Arm {
203                    generation,
204                    deadline_ns,
205                })
206            }
207            TimerRegistration::Scheduled {
208                deadline_ns: current_deadline,
209                ..
210            } if deadline_ns == current_deadline => {
211                self.request_sequence = sequence;
212                Ok(TimerControlAction::None)
213            }
214            TimerRegistration::Scheduled { .. } => {
215                let generation = self.next_generation()?;
216                self.request_sequence = sequence;
217                self.generation = generation;
218                self.registration = TimerRegistration::Scheduled {
219                    generation,
220                    deadline_ns,
221                };
222                Ok(TimerControlAction::Replace {
223                    generation,
224                    deadline_ns,
225                })
226            }
227            TimerRegistration::Running { .. } => {
228                self.request_sequence = sequence;
229                self.pending = Some(PendingCommand::Reconcile {
230                    sequence,
231                    deadline_ns,
232                });
233                Ok(TimerControlAction::None)
234            }
235        }
236    }
237
238    /// Begin the scheduled generation, rejecting stale callbacks.
239    pub const fn begin(&mut self, generation: u64) -> bool {
240        match self.registration {
241            TimerRegistration::Scheduled {
242                generation: scheduled_generation,
243                ..
244            } if scheduled_generation == generation => {
245                self.registration = TimerRegistration::Running { generation };
246                true
247            }
248            TimerRegistration::Unregistered
249            | TimerRegistration::Scheduled { .. }
250            | TimerRegistration::Running { .. } => false,
251        }
252    }
253
254    /// Complete the running generation and arbitrate its proposed successor
255    /// against commands received during execution.
256    pub fn complete(
257        &mut self,
258        generation: u64,
259        directive_deadline_ns: Option<u64>,
260    ) -> Result<TimerControlAction, TimerControlError> {
261        if self.registration != (TimerRegistration::Running { generation }) {
262            return Err(TimerControlError::StaleCompletion);
263        }
264
265        let (deadline_ns, cancelled) = match self.pending {
266            Some(PendingCommand::Cancel { .. }) => (None, true),
267            Some(PendingCommand::Reconcile { deadline_ns, .. }) => (Some(deadline_ns), false),
268            Some(PendingCommand::Schedule { deadline_ns, .. }) => (
269                Some(
270                    directive_deadline_ns
271                        .map_or(deadline_ns, |directive| directive.min(deadline_ns)),
272                ),
273                false,
274            ),
275            None => (directive_deadline_ns, false),
276        };
277
278        let next_generation = if deadline_ns.is_some() {
279            Some(self.next_generation()?)
280        } else {
281            None
282        };
283
284        self.pending = None;
285        if let (Some(deadline_ns), Some(next_generation)) = (deadline_ns, next_generation) {
286            self.generation = next_generation;
287            self.registration = TimerRegistration::Scheduled {
288                generation: next_generation,
289                deadline_ns,
290            };
291            Ok(TimerControlAction::Arm {
292                generation: next_generation,
293                deadline_ns,
294            })
295        } else {
296            self.registration = TimerRegistration::Unregistered;
297            Ok(TimerControlAction::Disarm { cancelled })
298        }
299    }
300
301    fn next_generation(&self) -> Result<u64, TimerControlError> {
302        self.generation
303            .checked_add(1)
304            .ok_or(TimerControlError::GenerationExhausted)
305    }
306
307    fn next_request_sequence(&self) -> Result<u64, TimerControlError> {
308        self.request_sequence
309            .checked_add(1)
310            .ok_or(TimerControlError::RequestSequenceExhausted)
311    }
312}
313
314#[cfg(test)]
315mod tests {
316    use super::*;
317
318    fn arm(control: &mut TimerControl, deadline_ns: u64) -> u64 {
319        let TimerControlAction::Arm { generation, .. } = control
320            .schedule(deadline_ns)
321            .expect("initial schedule should succeed")
322        else {
323            panic!("initial schedule should arm");
324        };
325        generation
326    }
327
328    #[test]
329    fn duplicate_and_later_schedules_keep_one_earliest_handle() {
330        let mut control = TimerControl::default();
331        assert_eq!(arm(&mut control, 100), 1);
332        assert_eq!(control.schedule(100), Ok(TimerControlAction::None));
333        assert_eq!(control.schedule(200), Ok(TimerControlAction::None));
334        assert_eq!(
335            control.registration(),
336            TimerRegistration::Scheduled {
337                generation: 1,
338                deadline_ns: 100
339            }
340        );
341    }
342
343    #[test]
344    fn earlier_schedule_replaces_and_invalidates_old_generation() {
345        let mut control = TimerControl::default();
346        let old_generation = arm(&mut control, 100);
347        assert_eq!(
348            control.schedule(50),
349            Ok(TimerControlAction::Replace {
350                generation: 2,
351                deadline_ns: 50
352            })
353        );
354        assert!(!control.begin(old_generation));
355        assert!(control.begin(2));
356    }
357
358    #[test]
359    fn authoritative_reconciliation_can_move_scheduled_deadline_later() {
360        let mut control = TimerControl::default();
361        let old_generation = arm(&mut control, 100);
362        assert_eq!(
363            control.reconcile(200),
364            Ok(TimerControlAction::Replace {
365                generation: 2,
366                deadline_ns: 200
367            })
368        );
369        assert!(!control.begin(old_generation));
370        assert!(control.begin(2));
371    }
372
373    #[test]
374    fn authoritative_reconciliation_while_running_replaces_callback_deadline() {
375        let mut control = TimerControl::default();
376        let generation = arm(&mut control, 100);
377        assert!(control.begin(generation));
378        assert_eq!(control.reconcile(300), Ok(TimerControlAction::None));
379        assert_eq!(
380            control.complete(generation, Some(150)),
381            Ok(TimerControlAction::Arm {
382                generation: 2,
383                deadline_ns: 300
384            })
385        );
386    }
387
388    #[test]
389    fn schedule_while_running_waits_and_survives_callback_stop() {
390        let mut control = TimerControl::default();
391        let generation = arm(&mut control, 100);
392        assert!(control.begin(generation));
393        assert_eq!(control.schedule(90), Ok(TimerControlAction::None));
394        assert_eq!(
395            control.complete(generation, None),
396            Ok(TimerControlAction::Arm {
397                generation: 2,
398                deadline_ns: 90
399            })
400        );
401    }
402
403    #[test]
404    fn running_callback_can_request_its_own_cancellation() {
405        let mut control = TimerControl::default();
406        let generation = arm(&mut control, 100);
407        assert!(control.begin(generation));
408        assert_eq!(control.cancel(), Ok(TimerControlAction::None));
409        assert_eq!(
410            control.complete(generation, Some(200)),
411            Ok(TimerControlAction::Disarm { cancelled: true })
412        );
413        assert_eq!(control.registration(), TimerRegistration::Unregistered);
414    }
415
416    #[test]
417    fn running_schedule_coalesces_to_earliest_deadline() {
418        let mut control = TimerControl::default();
419        let generation = arm(&mut control, 100);
420        assert!(control.begin(generation));
421        assert_eq!(control.schedule(300), Ok(TimerControlAction::None));
422        assert_eq!(control.schedule(250), Ok(TimerControlAction::None));
423        assert_eq!(
424            control.complete(generation, Some(275)),
425            Ok(TimerControlAction::Arm {
426                generation: 2,
427                deadline_ns: 250
428            })
429        );
430    }
431
432    #[test]
433    fn later_cancel_suppresses_callback_rearm() {
434        let mut control = TimerControl::default();
435        let generation = arm(&mut control, 100);
436        assert!(control.begin(generation));
437        assert_eq!(control.schedule(80), Ok(TimerControlAction::None));
438        assert_eq!(control.cancel(), Ok(TimerControlAction::None));
439        assert_eq!(
440            control.complete(generation, Some(75)),
441            Ok(TimerControlAction::Disarm { cancelled: true })
442        );
443    }
444
445    #[test]
446    fn schedule_after_cancel_reenables_only_after_completion() {
447        let mut control = TimerControl::default();
448        let generation = arm(&mut control, 100);
449        assert!(control.begin(generation));
450        assert_eq!(control.cancel(), Ok(TimerControlAction::None));
451        assert_eq!(control.schedule(90), Ok(TimerControlAction::None));
452        assert_eq!(
453            control.complete(generation, Some(95)),
454            Ok(TimerControlAction::Arm {
455                generation: 2,
456                deadline_ns: 90
457            })
458        );
459    }
460
461    #[test]
462    fn scheduled_cancel_invalidates_consumed_generation() {
463        let mut control = TimerControl::default();
464        let generation = arm(&mut control, 100);
465        assert_eq!(control.cancel(), Ok(TimerControlAction::Clear));
466        assert!(!control.begin(generation));
467        assert_eq!(control.generation(), 2);
468        assert_eq!(control.registration(), TimerRegistration::Unregistered);
469    }
470
471    #[test]
472    fn stale_completion_cannot_change_current_registration() {
473        let mut control = TimerControl::default();
474        let generation = arm(&mut control, 100);
475        assert!(control.begin(generation));
476        assert_eq!(
477            control.complete(generation + 1, None),
478            Err(TimerControlError::StaleCompletion)
479        );
480        assert_eq!(
481            control.registration(),
482            TimerRegistration::Running { generation }
483        );
484    }
485
486    #[test]
487    fn exhausted_request_sequence_fails_without_mutating_registration() {
488        let mut control = TimerControl {
489            request_sequence: u64::MAX,
490            ..TimerControl::default()
491        };
492
493        assert_eq!(
494            control.schedule(100),
495            Err(TimerControlError::RequestSequenceExhausted)
496        );
497        assert_eq!(control.registration(), TimerRegistration::Unregistered);
498    }
499
500    #[test]
501    fn exhausted_generation_fails_without_replacing_current_handle() {
502        let mut control = TimerControl {
503            generation: u64::MAX,
504            registration: TimerRegistration::Scheduled {
505                generation: u64::MAX,
506                deadline_ns: 100,
507            },
508            ..TimerControl::default()
509        };
510
511        assert_eq!(
512            control.schedule(50),
513            Err(TimerControlError::GenerationExhausted)
514        );
515        assert_eq!(
516            control.registration(),
517            TimerRegistration::Scheduled {
518                generation: u64::MAX,
519                deadline_ns: 100
520            }
521        );
522    }
523}