1use crate::{
8 DeclarationLifetime, InactiveReason, OrdinaryRuntimeStateSnapshot, ScheduleError, TimerCadence,
9 TimerCompletion, TimerCompletionOutcome, TimerControl, TimerControlAction, TimerControlError,
10 TimerControlFailure, TimerDirective, TimerDirectiveSnapshot, TimerEpoch, TimerIdentity,
11 TimerObservabilitySnapshot, TimerPolicy, TimerRunResult, TimerRuntimeStateSnapshot,
12 TimerSchedule, TimerSchedulingMode, TimerSnapshot, WatchdogAttemptSnapshot,
13 WatchdogAttemptStatus, WatchdogDecision, WatchdogRunResult, WatchdogRuntimeStateSnapshot,
14 platform::TimerHandle,
15 runtime::{TimerContext, TimerFuture},
16};
17use std::{cell::RefCell, collections::BTreeMap, rc::Rc};
18use thiserror::Error;
19
20pub const MAX_TIMER_REGISTRATIONS: usize = 64;
22
23#[derive(Debug, Eq, PartialEq)]
25pub struct RegistrationClaim {
26 identity: TimerIdentity,
27 claim_generation: u64,
28}
29
30impl RegistrationClaim {
31 pub(crate) const fn identity(&self) -> &TimerIdentity {
32 &self.identity
33 }
34
35 pub(crate) const fn claim_generation(&self) -> u64 {
36 self.claim_generation
37 }
38
39 pub(crate) const fn delegated(identity: TimerIdentity, claim_generation: u64) -> Self {
40 Self {
41 identity,
42 claim_generation,
43 }
44 }
45}
46
47#[derive(Clone, Copy, Debug, Eq, PartialEq)]
49pub enum CallbackRole {
50 OrdinaryWork,
51 WatchdogScheduler,
52 WatchdogWork,
53}
54
55#[derive(Clone, Debug, Eq, PartialEq)]
57pub struct CallbackToken {
58 identity: TimerIdentity,
59 claim_generation: u64,
60 callback_generation: u64,
61 role: CallbackRole,
62}
63
64impl CallbackToken {
65 const fn new(
66 identity: TimerIdentity,
67 claim_generation: u64,
68 callback_generation: u64,
69 role: CallbackRole,
70 ) -> Self {
71 Self {
72 identity,
73 claim_generation,
74 callback_generation,
75 role,
76 }
77 }
78
79 pub(crate) const fn identity(&self) -> &TimerIdentity {
80 &self.identity
81 }
82
83 #[cfg(test)]
84 pub(crate) const fn callback_generation(&self) -> u64 {
85 self.callback_generation
86 }
87
88 pub(crate) const fn claim_generation(&self) -> u64 {
89 self.claim_generation
90 }
91
92 pub(crate) const fn role(&self) -> CallbackRole {
93 self.role
94 }
95}
96
97#[derive(Debug, Eq, PartialEq)]
99pub enum RegistryEffect {
100 None,
101 ArmWakeup {
102 token: CallbackToken,
103 deadline_ns: u64,
104 delay_ns: u64,
105 replace: bool,
106 },
107 ClearCallbacks {
108 identity: TimerIdentity,
109 clear_wakeup: bool,
110 clear_work: bool,
111 },
112 DispatchWatchdog {
113 successor: CallbackToken,
114 successor_deadline_ns: u64,
115 successor_delay_ns: u64,
116 work: CallbackToken,
117 },
118}
119
120#[derive(Debug, Eq, PartialEq)]
122pub struct RegistryTransition {
123 effect: RegistryEffect,
124 failure: Option<TimerControlFailure>,
125}
126
127impl RegistryTransition {
128 const fn normal(effect: RegistryEffect) -> Self {
129 Self {
130 effect,
131 failure: None,
132 }
133 }
134
135 const fn terminal(effect: RegistryEffect, failure: TimerControlFailure) -> Self {
136 Self {
137 effect,
138 failure: Some(failure),
139 }
140 }
141
142 pub(crate) const fn effect(&self) -> &RegistryEffect {
143 &self.effect
144 }
145
146 pub(crate) const fn failure(&self) -> Option<TimerControlFailure> {
147 self.failure
148 }
149
150 pub(crate) fn into_effect(self) -> RegistryEffect {
151 self.effect
152 }
153}
154
155#[derive(Clone, Copy, Debug, Eq, PartialEq)]
157pub enum CallbackAcceptance {
158 Accepted,
159 Stale,
160}
161
162#[non_exhaustive]
164#[derive(Clone, Debug, Eq, Error, PartialEq)]
165pub enum RegisterError {
166 #[error("timer identity is already registered: {0:?}")]
168 IdentityAlreadyRegistered(TimerIdentity),
169 #[error("timer registry capacity of {max} declarations has been reached")]
171 CapacityExceeded {
172 max: usize,
174 },
175 #[error("timer registration claim generation exhausted")]
177 ClaimGenerationExhausted,
178}
179
180#[non_exhaustive]
182#[derive(Clone, Debug, Eq, Error, PartialEq)]
183pub enum RegistryError {
184 #[error("timer registration is no longer present")]
185 UnknownRegistration,
186 #[error("timer registration claim is stale")]
187 StaleRegistration,
188 #[error("timer operation is not legal for policy {actual}")]
189 WrongPolicy { actual: &'static str },
190 #[error("timer callback token is stale")]
191 StaleCallback,
192 #[error("timer registration has no ordinary callback")]
193 MissingCallback,
194 #[error("timer registration already owns the provider handle for this callback role")]
195 ProviderHandleAlreadyOwned,
196 #[error(transparent)]
197 Schedule(#[from] ScheduleError),
198}
199
200pub type OrdinaryCallback = Rc<RefCell<Box<dyn FnMut(TimerContext) -> TimerFuture>>>;
201pub type WatchdogCallback = Rc<RefCell<Box<dyn FnMut(TimerContext) -> WatchdogRunResult>>>;
202
203enum EntryCallback {
204 #[cfg(test)]
205 None,
206 Ordinary(OrdinaryCallback),
207 Watchdog(WatchdogCallback),
208}
209
210struct OwnedProviderHandle {
211 callback_generation: u64,
212 role: CallbackRole,
213 handle: TimerHandle,
214}
215
216pub struct ProviderHandle {
218 token: CallbackToken,
219 handle: TimerHandle,
220}
221
222impl ProviderHandle {
223 pub fn into_parts(self) -> (CallbackToken, TimerHandle) {
224 (self.token, self.handle)
225 }
226}
227
228#[derive(Default)]
230pub struct ProviderHandles {
231 wakeup: Option<ProviderHandle>,
232 work: Option<ProviderHandle>,
233}
234
235impl ProviderHandles {
236 pub const fn from_parts(wakeup: Option<ProviderHandle>, work: Option<ProviderHandle>) -> Self {
237 Self { wakeup, work }
238 }
239
240 pub const fn take_wakeup(&mut self) -> Option<ProviderHandle> {
241 self.wakeup.take()
242 }
243
244 pub const fn take_work(&mut self) -> Option<ProviderHandle> {
245 self.work.take()
246 }
247}
248
249#[derive(Clone, Copy, Debug, Eq, PartialEq)]
250struct PendingSchedule {
251 deadline_ns: u64,
252 requested_delay_ns: Option<u64>,
253 mode: TimerSchedulingMode,
254}
255
256#[derive(Clone, Copy, Debug, Eq, PartialEq)]
257enum OrdinaryPending {
258 Cancel,
259 Reconcile(PendingSchedule),
260 Unregister,
261 Schedule(PendingSchedule),
262}
263
264#[derive(Clone, Copy, Debug, Eq, PartialEq)]
265enum OrdinaryRequest {
266 Ensure,
267 Reconcile,
268}
269
270#[derive(Clone, Copy, Debug, Eq, PartialEq)]
271enum WatchdogPending {
272 Cancel,
273 Ensure,
274 Unregister,
275}
276
277#[derive(Debug)]
278enum EntryControl {
279 Ordinary {
280 control: TimerControl,
281 pending: Option<OrdinaryPending>,
282 inactive_reason: InactiveReason,
283 },
284 Watchdog(WatchdogControl),
285}
286
287#[derive(Clone, Copy, Debug, Eq, PartialEq)]
288enum WatchdogState {
289 Inactive,
290 Scheduled {
291 scheduler_generation: u64,
292 deadline_ns: u64,
293 },
294 AwaitingWork {
295 successor_generation: u64,
296 successor_deadline_ns: u64,
297 attempt_generation: u64,
298 attempt_status: WatchdogAttemptStatus,
299 },
300}
301
302#[derive(Debug)]
303struct WatchdogControl {
304 scheduler_generation: u64,
305 attempt_generation: u64,
306 request_sequence: u64,
307 state: WatchdogState,
308 pending: Option<WatchdogPending>,
309 inactive_reason: InactiveReason,
310}
311
312impl Default for WatchdogControl {
313 fn default() -> Self {
314 Self {
315 scheduler_generation: 0,
316 attempt_generation: 0,
317 request_sequence: 0,
318 state: WatchdogState::Inactive,
319 pending: None,
320 inactive_reason: InactiveReason::NeverScheduled,
321 }
322 }
323}
324
325struct Entry {
326 claim_generation: u64,
327 policy: TimerPolicy,
328 lifetime: DeclarationLifetime,
329 control: EntryControl,
330 scheduling_mode: TimerSchedulingMode,
331 latest_directive: Option<TimerDirectiveSnapshot>,
332 latest_requested_delay_ns: Option<u64>,
333 latest_armed_delay_ns: Option<u64>,
334 confirmed_wakeup_generation: Option<u64>,
335 confirmed_work_generation: Option<u64>,
336 callback: EntryCallback,
337 wakeup: Option<OwnedProviderHandle>,
338 work: Option<OwnedProviderHandle>,
339 observability: TimerObservabilitySnapshot,
340}
341
342impl Entry {
343 fn new(
344 claim_generation: u64,
345 policy: TimerPolicy,
346 lifetime: DeclarationLifetime,
347 epoch: TimerEpoch,
348 callback: EntryCallback,
349 ) -> Self {
350 let (control, scheduling_mode) = match policy {
351 TimerPolicy::Once => (
352 EntryControl::Ordinary {
353 control: TimerControl::default(),
354 pending: None,
355 inactive_reason: InactiveReason::NeverScheduled,
356 },
357 TimerSchedulingMode::Once,
358 ),
359 TimerPolicy::AfterCompletion { .. } => (
360 EntryControl::Ordinary {
361 control: TimerControl::default(),
362 pending: None,
363 inactive_reason: InactiveReason::NeverScheduled,
364 },
365 TimerSchedulingMode::AfterCompletion,
366 ),
367 TimerPolicy::Watchdog { .. } => (
368 EntryControl::Watchdog(WatchdogControl::default()),
369 TimerSchedulingMode::Watchdog,
370 ),
371 };
372
373 Self {
374 claim_generation,
375 policy,
376 lifetime,
377 control,
378 scheduling_mode,
379 latest_directive: None,
380 latest_requested_delay_ns: None,
381 latest_armed_delay_ns: None,
382 confirmed_wakeup_generation: None,
383 confirmed_work_generation: None,
384 callback,
385 wakeup: None,
386 work: None,
387 observability: TimerObservabilitySnapshot::new(epoch),
388 }
389 }
390
391 const fn snapshot(&self, identity: TimerIdentity) -> TimerSnapshot {
392 let state = match &self.control {
393 EntryControl::Ordinary {
394 control,
395 inactive_reason,
396 ..
397 } => match control.registration() {
398 crate::TimerRegistration::Unregistered => TimerRuntimeStateSnapshot::Inactive {
399 reason: *inactive_reason,
400 },
401 crate::TimerRegistration::Scheduled {
402 generation,
403 deadline_ns,
404 } => TimerRuntimeStateSnapshot::Ordinary(OrdinaryRuntimeStateSnapshot::Scheduled {
405 generation,
406 deadline_ns,
407 }),
408 crate::TimerRegistration::Running { generation } => {
409 TimerRuntimeStateSnapshot::Ordinary(OrdinaryRuntimeStateSnapshot::Running {
410 generation,
411 })
412 }
413 },
414 EntryControl::Watchdog(control) => match control.state {
415 WatchdogState::Inactive => TimerRuntimeStateSnapshot::Inactive {
416 reason: control.inactive_reason,
417 },
418 WatchdogState::Scheduled {
419 scheduler_generation,
420 deadline_ns,
421 } => TimerRuntimeStateSnapshot::Watchdog(WatchdogRuntimeStateSnapshot::Scheduled {
422 scheduler_generation,
423 deadline_ns,
424 }),
425 WatchdogState::AwaitingWork {
426 successor_generation,
427 successor_deadline_ns,
428 attempt_generation,
429 attempt_status,
430 } => TimerRuntimeStateSnapshot::Watchdog(
431 WatchdogRuntimeStateSnapshot::AwaitingWork {
432 successor_generation,
433 successor_deadline_ns,
434 attempt: WatchdogAttemptSnapshot::new(attempt_generation, attempt_status),
435 },
436 ),
437 },
438 };
439
440 TimerSnapshot::new(
441 identity,
442 self.policy,
443 self.lifetime,
444 state,
445 self.scheduling_mode,
446 self.latest_directive,
447 self.latest_requested_delay_ns,
448 self.latest_armed_delay_ns,
449 &self.observability,
450 )
451 }
452}
453
454pub struct TimerRegistry {
456 epoch: TimerEpoch,
457 next_claim_generation: u64,
458 entries: BTreeMap<TimerIdentity, Entry>,
459}
460
461impl TimerRegistry {
462 pub(crate) const fn new(epoch: TimerEpoch) -> Self {
463 Self {
464 epoch,
465 next_claim_generation: 0,
466 entries: BTreeMap::new(),
467 }
468 }
469
470 #[cfg(test)]
471 pub(crate) fn len(&self) -> usize {
472 self.entries.len()
473 }
474
475 #[cfg(test)]
476 pub(crate) fn is_empty(&self) -> bool {
477 self.entries.is_empty()
478 }
479
480 pub(crate) const fn epoch(&self) -> TimerEpoch {
481 self.epoch
482 }
483
484 #[cfg(test)]
485 pub(crate) fn register_once(
486 &mut self,
487 identity: TimerIdentity,
488 lifetime: DeclarationLifetime,
489 ) -> Result<RegistrationClaim, RegisterError> {
490 self.register(identity, TimerPolicy::Once, lifetime, EntryCallback::None)
491 }
492
493 #[cfg(test)]
494 pub(crate) fn register_after_completion(
495 &mut self,
496 identity: TimerIdentity,
497 cadence: TimerCadence,
498 lifetime: DeclarationLifetime,
499 ) -> Result<RegistrationClaim, RegisterError> {
500 self.register(
501 identity,
502 TimerPolicy::AfterCompletion { cadence },
503 lifetime,
504 EntryCallback::None,
505 )
506 }
507
508 #[cfg(test)]
509 pub(crate) fn register_watchdog(
510 &mut self,
511 identity: TimerIdentity,
512 cadence: TimerCadence,
513 lifetime: DeclarationLifetime,
514 ) -> Result<RegistrationClaim, RegisterError> {
515 self.register(
516 identity,
517 TimerPolicy::Watchdog { cadence },
518 lifetime,
519 EntryCallback::None,
520 )
521 }
522
523 pub(crate) fn register_once_with_callback(
524 &mut self,
525 identity: TimerIdentity,
526 lifetime: DeclarationLifetime,
527 callback: OrdinaryCallback,
528 ) -> Result<RegistrationClaim, RegisterError> {
529 self.register(
530 identity,
531 TimerPolicy::Once,
532 lifetime,
533 EntryCallback::Ordinary(callback),
534 )
535 }
536
537 pub(crate) fn register_after_completion_with_callback(
538 &mut self,
539 identity: TimerIdentity,
540 cadence: TimerCadence,
541 lifetime: DeclarationLifetime,
542 callback: OrdinaryCallback,
543 ) -> Result<RegistrationClaim, RegisterError> {
544 self.register(
545 identity,
546 TimerPolicy::AfterCompletion { cadence },
547 lifetime,
548 EntryCallback::Ordinary(callback),
549 )
550 }
551
552 pub(crate) fn register_watchdog_with_callback(
553 &mut self,
554 identity: TimerIdentity,
555 cadence: TimerCadence,
556 lifetime: DeclarationLifetime,
557 callback: WatchdogCallback,
558 ) -> Result<RegistrationClaim, RegisterError> {
559 self.register(
560 identity,
561 TimerPolicy::Watchdog { cadence },
562 lifetime,
563 EntryCallback::Watchdog(callback),
564 )
565 }
566
567 fn register(
568 &mut self,
569 identity: TimerIdentity,
570 policy: TimerPolicy,
571 lifetime: DeclarationLifetime,
572 callback: EntryCallback,
573 ) -> Result<RegistrationClaim, RegisterError> {
574 if self.entries.contains_key(&identity) {
575 return Err(RegisterError::IdentityAlreadyRegistered(identity));
576 }
577 if self.entries.len() == MAX_TIMER_REGISTRATIONS {
578 return Err(RegisterError::CapacityExceeded {
579 max: MAX_TIMER_REGISTRATIONS,
580 });
581 }
582 let claim_generation = self
583 .next_claim_generation
584 .checked_add(1)
585 .ok_or(RegisterError::ClaimGenerationExhausted)?;
586
587 self.next_claim_generation = claim_generation;
588 self.entries.insert(
589 identity.clone(),
590 Entry::new(claim_generation, policy, lifetime, self.epoch, callback),
591 );
592 Ok(RegistrationClaim {
593 identity,
594 claim_generation,
595 })
596 }
597
598 pub(crate) fn ensure_once(
599 &mut self,
600 claim: &RegistrationClaim,
601 now_ns: u64,
602 schedule: TimerSchedule,
603 ) -> Result<RegistryTransition, RegistryError> {
604 let resolved = schedule.resolve(now_ns)?;
605 let mode = match schedule {
606 TimerSchedule::After(_) => TimerSchedulingMode::Once,
607 TimerSchedule::At(_) => TimerSchedulingMode::Deadline,
608 };
609 self.schedule_ordinary(
610 claim,
611 now_ns,
612 PendingSchedule {
613 deadline_ns: resolved.deadline_ns,
614 requested_delay_ns: resolved.requested_delay_ns,
615 mode,
616 },
617 true,
618 )
619 }
620
621 pub(crate) fn reconcile_ordinary(
626 &mut self,
627 claim: &RegistrationClaim,
628 now_ns: u64,
629 schedule: Option<TimerSchedule>,
630 ) -> Result<RegistryTransition, RegistryError> {
631 self.validate_ordinary_claim(claim)?;
632 let Some(schedule) = schedule else {
633 return self.cancel(claim);
634 };
635 let resolved = schedule.resolve(now_ns)?;
636 let mode = match schedule {
637 TimerSchedule::After(_) => TimerSchedulingMode::Once,
638 TimerSchedule::At(_) => TimerSchedulingMode::Deadline,
639 };
640 self.request_ordinary(
641 claim,
642 now_ns,
643 PendingSchedule {
644 deadline_ns: resolved.deadline_ns,
645 requested_delay_ns: resolved.requested_delay_ns,
646 mode,
647 },
648 false,
649 OrdinaryRequest::Reconcile,
650 )
651 }
652
653 pub(crate) fn validate_ordinary_claim(
654 &self,
655 claim: &RegistrationClaim,
656 ) -> Result<(), RegistryError> {
657 let entry = self.entry(claim)?;
658 if matches!(entry.control, EntryControl::Ordinary { .. }) {
659 Ok(())
660 } else {
661 Err(RegistryError::WrongPolicy {
662 actual: entry.policy.label(),
663 })
664 }
665 }
666
667 pub(crate) fn ensure_recurring(
668 &mut self,
669 claim: &RegistrationClaim,
670 now_ns: u64,
671 ) -> Result<RegistryTransition, RegistryError> {
672 let entry = self.entry(claim)?;
673 let (cadence, mode, ordinary, already_scheduled) = match entry.policy {
674 TimerPolicy::Once => {
675 return Err(RegistryError::WrongPolicy { actual: "once" });
676 }
677 TimerPolicy::AfterCompletion { cadence } => {
678 let already_scheduled = matches!(
679 entry.control,
680 EntryControl::Ordinary {
681 ref control,
682 ..
683 } if matches!(
684 control.registration(),
685 crate::TimerRegistration::Scheduled { .. }
686 )
687 );
688 (
689 cadence,
690 TimerSchedulingMode::AfterCompletion,
691 true,
692 already_scheduled,
693 )
694 }
695 TimerPolicy::Watchdog { cadence } => {
696 (cadence, TimerSchedulingMode::Watchdog, false, false)
697 }
698 };
699 if ordinary {
700 if already_scheduled {
701 let entry = self.entry_mut(claim)?;
702 entry.observability.counters_mut().record_schedule_request();
703 entry.observability.counters_mut().record_coalesced();
704 entry.latest_requested_delay_ns = Some(cadence.as_nanos());
705 return Ok(RegistryTransition::normal(RegistryEffect::None));
706 }
707 let deadline_ns = cadence.deadline_after(now_ns)?;
708 self.schedule_ordinary(
709 claim,
710 now_ns,
711 PendingSchedule {
712 deadline_ns,
713 requested_delay_ns: Some(cadence.as_nanos()),
714 mode,
715 },
716 false,
717 )
718 } else {
719 self.ensure_watchdog(claim, now_ns, cadence)
720 }
721 }
722
723 fn schedule_ordinary(
724 &mut self,
725 claim: &RegistrationClaim,
726 now_ns: u64,
727 requested: PendingSchedule,
728 require_once: bool,
729 ) -> Result<RegistryTransition, RegistryError> {
730 self.request_ordinary(
731 claim,
732 now_ns,
733 requested,
734 require_once,
735 OrdinaryRequest::Ensure,
736 )
737 }
738
739 fn request_ordinary(
740 &mut self,
741 claim: &RegistrationClaim,
742 now_ns: u64,
743 requested: PendingSchedule,
744 require_once: bool,
745 request: OrdinaryRequest,
746 ) -> Result<RegistryTransition, RegistryError> {
747 let entry = self.entry_mut(claim)?;
748 if require_once && !matches!(entry.policy, TimerPolicy::Once) {
749 return Err(RegistryError::WrongPolicy {
750 actual: entry.policy.label(),
751 });
752 }
753 if !matches!(entry.control, EntryControl::Ordinary { .. }) {
754 return Err(RegistryError::WrongPolicy {
755 actual: entry.policy.label(),
756 });
757 }
758
759 entry.observability.counters_mut().record_schedule_request();
760 entry.latest_requested_delay_ns = requested.requested_delay_ns;
761
762 let (action, was_running) = match &mut entry.control {
763 EntryControl::Ordinary { control, .. } => {
764 let was_running = matches!(
765 control.registration(),
766 crate::TimerRegistration::Running { .. }
767 );
768 let action = match request {
769 OrdinaryRequest::Ensure => control.schedule(requested.deadline_ns),
770 OrdinaryRequest::Reconcile => control.reconcile(requested.deadline_ns),
771 };
772 (action, was_running)
773 }
774 EntryControl::Watchdog(_) => {
775 return Err(RegistryError::WrongPolicy {
776 actual: entry.policy.label(),
777 });
778 }
779 };
780
781 let action = match action {
782 Ok(action) => action,
783 Err(error) => {
784 return Ok(terminal_ordinary(entry, claim.identity.clone(), error));
785 }
786 };
787
788 if was_running {
789 let EntryControl::Ordinary { pending, .. } = &mut entry.control else {
790 return Err(RegistryError::WrongPolicy {
791 actual: entry.policy.label(),
792 });
793 };
794 *pending = Some(select_pending_ordinary(*pending, request, requested));
795 }
796 if matches!(request, OrdinaryRequest::Reconcile) {
797 entry.scheduling_mode = requested.mode;
798 }
799
800 Ok(apply_ordinary_action(
801 entry,
802 claim.identity.clone(),
803 now_ns,
804 action,
805 requested,
806 ))
807 }
808
809 fn ensure_watchdog(
810 &mut self,
811 claim: &RegistrationClaim,
812 now_ns: u64,
813 cadence: TimerCadence,
814 ) -> Result<RegistryTransition, RegistryError> {
815 let entry = self.entry_mut(claim)?;
816 if !matches!(entry.policy, TimerPolicy::Watchdog { .. }) {
817 return Err(RegistryError::WrongPolicy {
818 actual: entry.policy.label(),
819 });
820 }
821 entry.observability.counters_mut().record_schedule_request();
822 entry.latest_requested_delay_ns = Some(cadence.as_nanos());
823
824 let EntryControl::Watchdog(control) = &mut entry.control else {
825 return Err(RegistryError::WrongPolicy {
826 actual: entry.policy.label(),
827 });
828 };
829 match control.state {
830 WatchdogState::Inactive => {
831 let deadline_ns = cadence.deadline_after(now_ns)?;
832 let Some(generation) = control.scheduler_generation.checked_add(1) else {
833 control.inactive_reason =
834 InactiveReason::ControlFailure(TimerControlFailure::GenerationExhausted);
835 return Ok(RegistryTransition::terminal(
836 RegistryEffect::None,
837 TimerControlFailure::GenerationExhausted,
838 ));
839 };
840 control.scheduler_generation = generation;
841 control.state = WatchdogState::Scheduled {
842 scheduler_generation: generation,
843 deadline_ns,
844 };
845 control.pending = None;
846 entry.scheduling_mode = TimerSchedulingMode::Watchdog;
847 Ok(RegistryTransition::normal(RegistryEffect::ArmWakeup {
848 token: token_for(claim, generation, CallbackRole::WatchdogScheduler),
849 deadline_ns,
850 delay_ns: deadline_ns.saturating_sub(now_ns),
851 replace: false,
852 }))
853 }
854 WatchdogState::Scheduled { .. }
855 | WatchdogState::AwaitingWork {
856 attempt_status: WatchdogAttemptStatus::Dispatched,
857 ..
858 } => {
859 entry.observability.counters_mut().record_coalesced();
860 Ok(RegistryTransition::normal(RegistryEffect::None))
861 }
862 WatchdogState::AwaitingWork {
863 attempt_status: WatchdogAttemptStatus::Running,
864 ..
865 } => {
866 let Some(sequence) = control.request_sequence.checked_add(1) else {
867 control.state = WatchdogState::Inactive;
868 control.inactive_reason = InactiveReason::ControlFailure(
869 TimerControlFailure::RequestSequenceExhausted,
870 );
871 return Ok(RegistryTransition::terminal(
872 clear_callbacks(claim.identity.clone(), true, false),
873 TimerControlFailure::RequestSequenceExhausted,
874 ));
875 };
876 control.request_sequence = sequence;
877 if !matches!(control.pending, Some(WatchdogPending::Unregister)) {
878 control.pending = Some(WatchdogPending::Ensure);
879 }
880 entry.observability.counters_mut().record_coalesced();
881 Ok(RegistryTransition::normal(RegistryEffect::None))
882 }
883 }
884 }
885
886 pub(crate) fn cancel(
887 &mut self,
888 claim: &RegistrationClaim,
889 ) -> Result<RegistryTransition, RegistryError> {
890 let identity = claim.identity.clone();
891 let (transition, remove) = {
892 let entry = self.entry_mut(claim)?;
893 match &mut entry.control {
894 EntryControl::Ordinary {
895 control,
896 pending,
897 inactive_reason,
898 } => {
899 let before = control.registration();
900 let action = match control.cancel() {
901 Ok(action) => action,
902 Err(error) => {
903 return Ok(terminal_ordinary(entry, identity.clone(), error));
904 }
905 };
906 let mut remove = false;
907 let transition = match (before, action) {
908 (crate::TimerRegistration::Unregistered, TimerControlAction::None) => {
909 RegistryTransition::normal(RegistryEffect::None)
910 }
911 (crate::TimerRegistration::Running { .. }, TimerControlAction::None) => {
912 if !matches!(*pending, Some(OrdinaryPending::Unregister)) {
913 *pending = Some(OrdinaryPending::Cancel);
914 }
915 RegistryTransition::normal(RegistryEffect::None)
916 }
917 (_, TimerControlAction::Clear) => {
918 *inactive_reason = InactiveReason::Cancelled;
919 entry.observability.counters_mut().record_cancellation();
920 remove =
921 matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
922 RegistryTransition::normal(clear_callbacks(
923 identity.clone(),
924 true,
925 false,
926 ))
927 }
928 (crate::TimerRegistration::Scheduled { .. }, TimerControlAction::None)
929 | (
930 _,
931 TimerControlAction::Arm { .. }
932 | TimerControlAction::Replace { .. }
933 | TimerControlAction::Disarm { .. },
934 ) => {
935 let clear_wakeup = control.terminate();
936 *pending = None;
937 *inactive_reason = InactiveReason::ControlFailure(
938 TimerControlFailure::DirectiveNotAllowed,
939 );
940 RegistryTransition::terminal(
941 clear_callbacks(identity.clone(), clear_wakeup, false),
942 TimerControlFailure::DirectiveNotAllowed,
943 )
944 }
945 };
946 (transition, remove)
947 }
948 EntryControl::Watchdog(control) => {
949 let (transition, changed) = cancel_watchdog(control, &identity);
950 if changed && transition.failure().is_none() {
951 entry.observability.counters_mut().record_cancellation();
952 }
953 let stopped = changed || transition.failure().is_some();
954 (
955 transition,
956 stopped && matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped),
957 )
958 }
959 }
960 };
961
962 if remove {
963 self.entries.remove(&identity);
964 }
965 Ok(transition)
966 }
967
968 #[allow(clippy::needless_pass_by_value)] pub(crate) fn unregister(
975 &mut self,
976 claim: RegistrationClaim,
977 ) -> Result<RegistryTransition, RegistryError> {
978 let identity = claim.identity.clone();
979 let running_role = {
980 let entry = self.entry(&claim)?;
981 match &entry.control {
982 EntryControl::Ordinary { control, .. }
983 if matches!(
984 control.registration(),
985 crate::TimerRegistration::Running { .. }
986 ) =>
987 {
988 Some(CallbackRole::OrdinaryWork)
989 }
990 EntryControl::Watchdog(control)
991 if matches!(
992 control.state,
993 WatchdogState::AwaitingWork {
994 attempt_status: WatchdogAttemptStatus::Running,
995 ..
996 }
997 ) =>
998 {
999 Some(CallbackRole::WatchdogWork)
1000 }
1001 EntryControl::Ordinary { .. } | EntryControl::Watchdog(_) => None,
1002 }
1003 };
1004
1005 match running_role {
1006 Some(CallbackRole::OrdinaryWork) => {
1007 let entry = self.entry_mut(&claim)?;
1008 let EntryControl::Ordinary {
1009 control,
1010 pending,
1011 inactive_reason,
1012 } = &mut entry.control
1013 else {
1014 return Err(RegistryError::WrongPolicy {
1015 actual: entry.policy.label(),
1016 });
1017 };
1018 let transition = match control.cancel() {
1019 Ok(TimerControlAction::None) => {
1020 *pending = Some(OrdinaryPending::Unregister);
1021 RegistryTransition::normal(RegistryEffect::None)
1022 }
1023 Ok(_) => RegistryTransition::terminal(
1024 RegistryEffect::None,
1025 TimerControlFailure::DirectiveNotAllowed,
1026 ),
1027 Err(error) => {
1028 let failure = map_control_failure(error);
1029 let clear_wakeup = control.terminate();
1030 *pending = None;
1031 *inactive_reason = InactiveReason::ControlFailure(failure);
1032 RegistryTransition::terminal(
1033 clear_callbacks(identity.clone(), clear_wakeup, false),
1034 failure,
1035 )
1036 }
1037 };
1038 if transition.failure().is_some() {
1039 self.entries.remove(&identity);
1040 }
1041 Ok(transition)
1042 }
1043 Some(CallbackRole::WatchdogWork) => {
1044 let entry = self.entry_mut(&claim)?;
1045 let EntryControl::Watchdog(control) = &mut entry.control else {
1046 return Err(RegistryError::WrongPolicy {
1047 actual: entry.policy.label(),
1048 });
1049 };
1050 let Some(sequence) = control.request_sequence.checked_add(1) else {
1051 control.state = WatchdogState::Inactive;
1052 control.inactive_reason = InactiveReason::ControlFailure(
1053 TimerControlFailure::RequestSequenceExhausted,
1054 );
1055 let transition = RegistryTransition::terminal(
1056 clear_callbacks(identity.clone(), true, false),
1057 TimerControlFailure::RequestSequenceExhausted,
1058 );
1059 self.entries.remove(&identity);
1060 return Ok(transition);
1061 };
1062 control.request_sequence = sequence;
1063 control.pending = Some(WatchdogPending::Unregister);
1064 Ok(RegistryTransition::normal(RegistryEffect::None))
1065 }
1066 Some(CallbackRole::WatchdogScheduler) => Err(RegistryError::StaleCallback),
1067 None => {
1068 let transition = self.cancel(&claim)?;
1069 self.entries.remove(&identity);
1070 Ok(transition)
1071 }
1072 }
1073 }
1074
1075 pub(crate) fn begin_ordinary(&mut self, token: &CallbackToken) -> CallbackAcceptance {
1076 let Some(entry) = self.entries.get_mut(token.identity()) else {
1077 return CallbackAcceptance::Stale;
1078 };
1079 if token.role != CallbackRole::OrdinaryWork
1080 || token.claim_generation != entry.claim_generation
1081 {
1082 entry.observability.counters_mut().record_stale_wakeup();
1083 return CallbackAcceptance::Stale;
1084 }
1085 let EntryControl::Ordinary { control, .. } = &mut entry.control else {
1086 entry.observability.counters_mut().record_stale_wakeup();
1087 return CallbackAcceptance::Stale;
1088 };
1089 if control.begin(token.callback_generation) {
1090 entry.observability.counters_mut().record_work_started();
1091 CallbackAcceptance::Accepted
1092 } else {
1093 entry.observability.counters_mut().record_stale_wakeup();
1094 CallbackAcceptance::Stale
1095 }
1096 }
1097
1098 #[allow(clippy::too_many_lines)] pub(crate) fn complete_ordinary(
1100 &mut self,
1101 token: &CallbackToken,
1102 now_ns: u64,
1103 result: TimerRunResult,
1104 ) -> Result<RegistryTransition, RegistryError> {
1105 let identity = token.identity.clone();
1106 let (transition, remove) = {
1107 let entry = self.entry_by_token_mut(token, CallbackRole::OrdinaryWork)?;
1108 let EntryControl::Ordinary {
1109 control,
1110 pending,
1111 inactive_reason,
1112 } = &mut entry.control
1113 else {
1114 return Err(RegistryError::StaleCallback);
1115 };
1116 if control.registration()
1117 != (crate::TimerRegistration::Running {
1118 generation: token.callback_generation,
1119 })
1120 {
1121 return Err(RegistryError::StaleCallback);
1122 }
1123
1124 let completion = result.completion();
1125 let pending_command = *pending;
1126 let terminal_pending = matches!(
1127 pending_command,
1128 Some(OrdinaryPending::Cancel | OrdinaryPending::Unregister)
1129 );
1130 if completion.outcome() == TimerCompletionOutcome::InvariantFailure {
1131 let transition =
1132 invariant_completion(entry, token.callback_generation, completion, now_ns);
1133 let remove = matches!(pending_command, Some(OrdinaryPending::Unregister))
1134 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1135 (transition, remove)
1136 } else if !terminal_pending
1137 && matches!(entry.policy, TimerPolicy::Once)
1138 && matches!(result.directive(), TimerDirective::RecurAfterCompletion)
1139 {
1140 let transition = terminal_completion(
1141 entry,
1142 token.callback_generation,
1143 completion.work_count(),
1144 now_ns,
1145 TimerControlFailure::DirectiveNotAllowed,
1146 );
1147 let remove = matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1148 (transition, remove)
1149 } else {
1150 let effective_directive = if terminal_pending {
1151 TimerDirective::Stop
1152 } else {
1153 result.directive()
1154 };
1155 let cadence = match entry.policy {
1156 TimerPolicy::AfterCompletion { cadence } => Some(cadence),
1157 TimerPolicy::Once => None,
1158 TimerPolicy::Watchdog { .. } => {
1159 return Err(RegistryError::StaleCallback);
1160 }
1161 };
1162 let resolved = match effective_directive.resolve(now_ns, cadence) {
1163 Ok(value) => value,
1164 Err(error) => {
1165 let transition = terminal_completion(
1166 entry,
1167 token.callback_generation,
1168 completion.work_count(),
1169 now_ns,
1170 map_schedule_failure(error),
1171 );
1172 let remove = matches!(pending_command, Some(OrdinaryPending::Unregister))
1173 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1174 return Ok(remove_after(
1175 &mut self.entries,
1176 &identity,
1177 transition,
1178 remove,
1179 ));
1180 }
1181 };
1182 let directive_snapshot = TimerDirectiveSnapshot::try_from(effective_directive)
1183 .map_err(RegistryError::Schedule)?;
1184 let callback_schedule = resolved.deadline_ns.map(|deadline_ns| PendingSchedule {
1185 deadline_ns,
1186 requested_delay_ns: resolved.requested_delay_ns,
1187 mode: directive_snapshot
1188 .scheduling_mode()
1189 .unwrap_or(entry.scheduling_mode),
1190 });
1191 let selected_schedule =
1192 select_completion_schedule(pending_command, callback_schedule);
1193 let action = match control.complete(
1194 token.callback_generation,
1195 selected_schedule.map(|value| value.deadline_ns),
1196 terminal_pending,
1197 ) {
1198 Ok(action) => action,
1199 Err(TimerControlError::StaleCompletion) => {
1200 return Err(RegistryError::StaleCallback);
1201 }
1202 Err(error) => {
1203 let transition = terminal_completion(
1204 entry,
1205 token.callback_generation,
1206 completion.work_count(),
1207 now_ns,
1208 map_control_failure(error),
1209 );
1210 let remove = matches!(pending_command, Some(OrdinaryPending::Unregister))
1211 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1212 return Ok(remove_after(
1213 &mut self.entries,
1214 &identity,
1215 transition,
1216 remove,
1217 ));
1218 }
1219 };
1220 *pending = None;
1221 entry.latest_directive = Some(directive_snapshot);
1222 entry.observability.record_completion(completion, now_ns);
1223
1224 let mut remove = false;
1225 let transition = match action {
1226 TimerControlAction::Arm {
1227 generation,
1228 deadline_ns,
1229 }
1230 | TimerControlAction::Replace {
1231 generation,
1232 deadline_ns,
1233 } => {
1234 let selected = selected_schedule.unwrap_or(PendingSchedule {
1235 deadline_ns,
1236 requested_delay_ns: None,
1237 mode: entry.scheduling_mode,
1238 });
1239 let replace = matches!(action, TimerControlAction::Replace { .. });
1240 entry.scheduling_mode = selected.mode;
1241 entry.latest_requested_delay_ns = selected.requested_delay_ns;
1242 RegistryTransition::normal(RegistryEffect::ArmWakeup {
1243 token: CallbackToken::new(
1244 identity.clone(),
1245 entry.claim_generation,
1246 generation,
1247 CallbackRole::OrdinaryWork,
1248 ),
1249 deadline_ns,
1250 delay_ns: deadline_ns.saturating_sub(now_ns),
1251 replace,
1252 })
1253 }
1254 TimerControlAction::Disarm { cancelled } => {
1255 *inactive_reason = if cancelled {
1256 entry.observability.counters_mut().record_cancellation();
1257 InactiveReason::Cancelled
1258 } else {
1259 InactiveReason::Stopped
1260 };
1261 remove = matches!(pending_command, Some(OrdinaryPending::Unregister))
1262 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1263 RegistryTransition::normal(RegistryEffect::None)
1264 }
1265 TimerControlAction::None | TimerControlAction::Clear => {
1266 let clear_wakeup = control.terminate();
1267 *inactive_reason = InactiveReason::ControlFailure(
1268 TimerControlFailure::DirectiveNotAllowed,
1269 );
1270 remove = matches!(pending_command, Some(OrdinaryPending::Unregister))
1271 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped);
1272 RegistryTransition::terminal(
1273 clear_callbacks(identity.clone(), clear_wakeup, false),
1274 TimerControlFailure::DirectiveNotAllowed,
1275 )
1276 }
1277 };
1278 (transition, remove)
1279 }
1280 };
1281
1282 Ok(remove_after(
1283 &mut self.entries,
1284 &identity,
1285 transition,
1286 remove,
1287 ))
1288 }
1289
1290 #[allow(clippy::too_many_lines)] pub(crate) fn begin_watchdog_scheduler(
1292 &mut self,
1293 token: &CallbackToken,
1294 now_ns: u64,
1295 ) -> RegistryTransition {
1296 let Some(entry) = self.entries.get_mut(token.identity()) else {
1297 return RegistryTransition::normal(RegistryEffect::None);
1298 };
1299 if token.role != CallbackRole::WatchdogScheduler
1300 || token.claim_generation != entry.claim_generation
1301 {
1302 entry.observability.counters_mut().record_stale_wakeup();
1303 return RegistryTransition::normal(RegistryEffect::None);
1304 }
1305 let TimerPolicy::Watchdog { cadence } = entry.policy else {
1306 entry.observability.counters_mut().record_stale_wakeup();
1307 return RegistryTransition::normal(RegistryEffect::None);
1308 };
1309 let EntryControl::Watchdog(control) = &mut entry.control else {
1310 entry.observability.counters_mut().record_stale_wakeup();
1311 return RegistryTransition::normal(RegistryEffect::None);
1312 };
1313
1314 let accepted = match control.state {
1315 WatchdogState::Scheduled {
1316 scheduler_generation,
1317 ..
1318 } => scheduler_generation == token.callback_generation,
1319 WatchdogState::AwaitingWork {
1320 successor_generation,
1321 attempt_status: WatchdogAttemptStatus::Dispatched,
1322 ..
1323 } => successor_generation == token.callback_generation,
1324 WatchdogState::Inactive
1325 | WatchdogState::AwaitingWork {
1326 attempt_status: WatchdogAttemptStatus::Running,
1327 ..
1328 } => false,
1329 };
1330 if !accepted {
1331 entry.observability.counters_mut().record_stale_wakeup();
1332 return RegistryTransition::normal(RegistryEffect::None);
1333 }
1334
1335 entry
1336 .observability
1337 .counters_mut()
1338 .record_scheduler_started();
1339 if matches!(control.state, WatchdogState::AwaitingWork { .. }) {
1340 entry.observability.record_unacknowledged(now_ns);
1341 }
1342 let Some(successor_generation) = control.scheduler_generation.checked_add(1) else {
1343 control.state = WatchdogState::Inactive;
1344 control.inactive_reason =
1345 InactiveReason::ControlFailure(TimerControlFailure::GenerationExhausted);
1346 return RegistryTransition::terminal(
1347 clear_callbacks(token.identity.clone(), false, true),
1348 TimerControlFailure::GenerationExhausted,
1349 );
1350 };
1351 let Some(attempt_generation) = control.attempt_generation.checked_add(1) else {
1352 control.state = WatchdogState::Inactive;
1353 control.inactive_reason =
1354 InactiveReason::ControlFailure(TimerControlFailure::GenerationExhausted);
1355 return RegistryTransition::terminal(
1356 clear_callbacks(token.identity.clone(), false, true),
1357 TimerControlFailure::GenerationExhausted,
1358 );
1359 };
1360 let Ok(successor_deadline_ns) = cadence.deadline_after(now_ns) else {
1361 control.state = WatchdogState::Inactive;
1362 control.inactive_reason =
1363 InactiveReason::ControlFailure(TimerControlFailure::DeadlineOverflow);
1364 return RegistryTransition::terminal(
1365 clear_callbacks(token.identity.clone(), false, true),
1366 TimerControlFailure::DeadlineOverflow,
1367 );
1368 };
1369
1370 control.scheduler_generation = successor_generation;
1371 control.attempt_generation = attempt_generation;
1372 control.state = WatchdogState::AwaitingWork {
1373 successor_generation,
1374 successor_deadline_ns,
1375 attempt_generation,
1376 attempt_status: WatchdogAttemptStatus::Dispatched,
1377 };
1378 control.pending = None;
1379 RegistryTransition::normal(RegistryEffect::DispatchWatchdog {
1380 successor: CallbackToken::new(
1381 token.identity.clone(),
1382 entry.claim_generation,
1383 successor_generation,
1384 CallbackRole::WatchdogScheduler,
1385 ),
1386 successor_deadline_ns,
1387 successor_delay_ns: cadence.as_nanos(),
1388 work: CallbackToken::new(
1389 token.identity.clone(),
1390 entry.claim_generation,
1391 attempt_generation,
1392 CallbackRole::WatchdogWork,
1393 ),
1394 })
1395 }
1396
1397 pub(crate) fn begin_watchdog_work(&mut self, token: &CallbackToken) -> CallbackAcceptance {
1398 let Some(entry) = self.entries.get_mut(token.identity()) else {
1399 return CallbackAcceptance::Stale;
1400 };
1401 if token.role != CallbackRole::WatchdogWork
1402 || token.claim_generation != entry.claim_generation
1403 {
1404 entry.observability.counters_mut().record_stale_work();
1405 return CallbackAcceptance::Stale;
1406 }
1407 let EntryControl::Watchdog(control) = &mut entry.control else {
1408 entry.observability.counters_mut().record_stale_work();
1409 return CallbackAcceptance::Stale;
1410 };
1411 match &mut control.state {
1412 WatchdogState::AwaitingWork {
1413 attempt_generation,
1414 attempt_status,
1415 ..
1416 } if *attempt_generation == token.callback_generation
1417 && *attempt_status == WatchdogAttemptStatus::Dispatched =>
1418 {
1419 *attempt_status = WatchdogAttemptStatus::Running;
1420 entry.observability.counters_mut().record_work_started();
1421 CallbackAcceptance::Accepted
1422 }
1423 WatchdogState::Inactive
1424 | WatchdogState::Scheduled { .. }
1425 | WatchdogState::AwaitingWork { .. } => {
1426 entry.observability.counters_mut().record_stale_work();
1427 CallbackAcceptance::Stale
1428 }
1429 }
1430 }
1431
1432 pub(crate) fn complete_watchdog_work(
1433 &mut self,
1434 token: &CallbackToken,
1435 now_ns: u64,
1436 result: WatchdogRunResult,
1437 ) -> Result<RegistryTransition, RegistryError> {
1438 let identity = token.identity.clone();
1439 let (transition, remove) = {
1440 let entry = self.entry_by_token_mut(token, CallbackRole::WatchdogWork)?;
1441 let EntryControl::Watchdog(control) = &mut entry.control else {
1442 return Err(RegistryError::StaleCallback);
1443 };
1444 let (successor_generation, successor_deadline_ns) = match control.state {
1445 WatchdogState::AwaitingWork {
1446 successor_generation,
1447 successor_deadline_ns,
1448 attempt_generation,
1449 attempt_status: WatchdogAttemptStatus::Running,
1450 } if attempt_generation == token.callback_generation => {
1451 (successor_generation, successor_deadline_ns)
1452 }
1453 WatchdogState::Inactive
1454 | WatchdogState::Scheduled { .. }
1455 | WatchdogState::AwaitingWork { .. } => {
1456 return Err(RegistryError::StaleCallback);
1457 }
1458 };
1459
1460 let completion = result.completion();
1461 entry.observability.record_completion(completion, now_ns);
1462 let decision = if completion.outcome() == TimerCompletionOutcome::InvariantFailure {
1463 WatchdogDecision::Stop
1464 } else {
1465 match control.pending {
1466 Some(WatchdogPending::Cancel | WatchdogPending::Unregister) => {
1467 WatchdogDecision::Stop
1468 }
1469 Some(WatchdogPending::Ensure) => WatchdogDecision::Continue,
1470 None => result.decision(),
1471 }
1472 };
1473 let cancelled = matches!(control.pending, Some(WatchdogPending::Cancel));
1474 let unregister = matches!(control.pending, Some(WatchdogPending::Unregister));
1475 control.pending = None;
1476
1477 match decision {
1478 WatchdogDecision::Continue => {
1479 control.state = WatchdogState::Scheduled {
1480 scheduler_generation: successor_generation,
1481 deadline_ns: successor_deadline_ns,
1482 };
1483 (RegistryTransition::normal(RegistryEffect::None), false)
1484 }
1485 WatchdogDecision::Stop => {
1486 control.state = WatchdogState::Inactive;
1487 control.inactive_reason = if cancelled {
1488 entry.observability.counters_mut().record_cancellation();
1489 InactiveReason::Cancelled
1490 } else if completion.outcome() == TimerCompletionOutcome::InvariantFailure {
1491 InactiveReason::InvariantFailure
1492 } else {
1493 InactiveReason::Stopped
1494 };
1495 (
1496 RegistryTransition::normal(clear_callbacks(identity.clone(), true, false)),
1497 unregister
1498 || matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped),
1499 )
1500 }
1501 }
1502 };
1503
1504 Ok(remove_after(
1505 &mut self.entries,
1506 &identity,
1507 transition,
1508 remove,
1509 ))
1510 }
1511
1512 pub(crate) fn snapshot(&self, identity: &TimerIdentity) -> Option<TimerSnapshot> {
1513 self.entries
1514 .get(identity)
1515 .map(|entry| entry.snapshot(identity.clone()))
1516 }
1517
1518 pub(crate) fn snapshots(&self) -> Vec<TimerSnapshot> {
1519 self.entries
1520 .iter()
1521 .map(|(identity, entry)| entry.snapshot(identity.clone()))
1522 .collect()
1523 }
1524
1525 pub(crate) fn consecutive_expected_failures(&self, identity: &TimerIdentity) -> Option<u64> {
1526 self.entries
1527 .get(identity)
1528 .map(|entry| entry.observability.consecutive_expected_failures())
1529 }
1530
1531 pub(crate) fn record_scheduler_instructions(
1532 &mut self,
1533 token: &CallbackToken,
1534 instructions: u64,
1535 ) {
1536 let Some(entry) = self.entries.get_mut(token.identity()) else {
1537 return;
1538 };
1539 if token.claim_generation == entry.claim_generation
1540 && token.role == CallbackRole::WatchdogScheduler
1541 && matches!(entry.control, EntryControl::Watchdog(_))
1542 {
1543 entry
1544 .observability
1545 .record_scheduler_instructions(instructions);
1546 }
1547 }
1548
1549 pub(crate) fn record_work_instructions(&mut self, token: &CallbackToken, instructions: u64) {
1550 let Some(entry) = self.entries.get_mut(token.identity()) else {
1551 return;
1552 };
1553 let role_matches = matches!(
1554 (&entry.control, token.role),
1555 (EntryControl::Ordinary { .. }, CallbackRole::OrdinaryWork)
1556 | (EntryControl::Watchdog(_), CallbackRole::WatchdogWork)
1557 );
1558 if token.claim_generation == entry.claim_generation && role_matches {
1559 entry.observability.record_work_instructions(instructions);
1560 }
1561 }
1562
1563 pub(crate) fn fail_registration(
1564 &mut self,
1565 claim: &RegistrationClaim,
1566 failure: TimerControlFailure,
1567 ) -> Result<ProviderHandles, RegistryError> {
1568 let identity = claim.identity.clone();
1569 let (handles, remove) = {
1570 let entry = self.entry_mut(claim)?;
1571 match &mut entry.control {
1572 EntryControl::Ordinary {
1573 control,
1574 pending,
1575 inactive_reason,
1576 } => {
1577 control.terminate();
1578 *pending = None;
1579 *inactive_reason = InactiveReason::ControlFailure(failure);
1580 }
1581 EntryControl::Watchdog(control) => {
1582 control.state = WatchdogState::Inactive;
1583 control.pending = None;
1584 control.inactive_reason = InactiveReason::ControlFailure(failure);
1585 }
1586 }
1587 let handles =
1588 ProviderHandles {
1589 wakeup: entry.wakeup.take().map(|owned| {
1590 detach_provider_handle(&identity, entry.claim_generation, owned)
1591 }),
1592 work: entry.work.take().map(|owned| {
1593 detach_provider_handle(&identity, entry.claim_generation, owned)
1594 }),
1595 };
1596 (
1597 handles,
1598 matches!(entry.lifetime, DeclarationLifetime::RemoveWhenStopped),
1599 )
1600 };
1601 if remove {
1602 self.entries.remove(&identity);
1603 }
1604 Ok(handles)
1605 }
1606
1607 pub(crate) fn ordinary_callback(
1608 &self,
1609 token: &CallbackToken,
1610 ) -> Result<OrdinaryCallback, RegistryError> {
1611 let entry = self.running_work_entry(token)?;
1612 match &entry.callback {
1613 EntryCallback::Ordinary(callback) => Ok(Rc::clone(callback)),
1614 EntryCallback::Watchdog(_) => Err(RegistryError::MissingCallback),
1615 #[cfg(test)]
1616 EntryCallback::None => Err(RegistryError::MissingCallback),
1617 }
1618 }
1619
1620 pub(crate) fn watchdog_callback(
1621 &self,
1622 token: &CallbackToken,
1623 ) -> Result<WatchdogCallback, RegistryError> {
1624 let entry = self.running_work_entry(token)?;
1625 match &entry.callback {
1626 EntryCallback::Watchdog(callback) => Ok(Rc::clone(callback)),
1627 EntryCallback::Ordinary(_) => Err(RegistryError::MissingCallback),
1628 #[cfg(test)]
1629 EntryCallback::None => Err(RegistryError::MissingCallback),
1630 }
1631 }
1632
1633 pub(crate) fn validate_running_context(
1634 &self,
1635 token: &CallbackToken,
1636 ) -> Result<(), RegistryError> {
1637 self.running_work_entry(token).map(|_| ())
1638 }
1639
1640 pub(crate) fn install_provider_handle(
1641 &mut self,
1642 token: &CallbackToken,
1643 handle: TimerHandle,
1644 ) -> Result<(), (RegistryError, TimerHandle)> {
1645 let entry = match self.entry_by_token_mut(token, token.role) {
1646 Ok(entry) => entry,
1647 Err(error) => return Err((error, handle)),
1648 };
1649 let valid = match (&entry.control, token.role) {
1650 (EntryControl::Ordinary { control, .. }, CallbackRole::OrdinaryWork) => matches!(
1651 control.registration(),
1652 crate::TimerRegistration::Scheduled { generation, .. }
1653 if generation == token.callback_generation
1654 ),
1655 (EntryControl::Watchdog(control), CallbackRole::WatchdogScheduler) => {
1656 matches!(
1657 control.state,
1658 WatchdogState::Scheduled {
1659 scheduler_generation,
1660 ..
1661 } if scheduler_generation == token.callback_generation
1662 ) || matches!(
1663 control.state,
1664 WatchdogState::AwaitingWork {
1665 successor_generation,
1666 ..
1667 } if successor_generation == token.callback_generation
1668 )
1669 }
1670 (EntryControl::Watchdog(control), CallbackRole::WatchdogWork) => matches!(
1671 control.state,
1672 WatchdogState::AwaitingWork {
1673 attempt_generation,
1674 attempt_status: WatchdogAttemptStatus::Dispatched,
1675 ..
1676 } if attempt_generation == token.callback_generation
1677 ),
1678 (
1679 EntryControl::Ordinary { .. },
1680 CallbackRole::WatchdogScheduler | CallbackRole::WatchdogWork,
1681 )
1682 | (EntryControl::Watchdog(_), CallbackRole::OrdinaryWork) => false,
1683 };
1684 if !valid {
1685 return Err((RegistryError::StaleCallback, handle));
1686 }
1687 let slot = match token.role {
1688 CallbackRole::OrdinaryWork | CallbackRole::WatchdogScheduler => &mut entry.wakeup,
1689 CallbackRole::WatchdogWork => &mut entry.work,
1690 };
1691 if slot.is_some() {
1692 return Err((RegistryError::ProviderHandleAlreadyOwned, handle));
1693 }
1694 *slot = Some(OwnedProviderHandle {
1695 callback_generation: token.callback_generation,
1696 role: token.role,
1697 handle,
1698 });
1699 Ok(())
1700 }
1701
1702 pub(crate) fn take_wakeup_handle(
1703 &mut self,
1704 identity: &TimerIdentity,
1705 ) -> Option<ProviderHandle> {
1706 let entry = self.entries.get_mut(identity)?;
1707 let owned = entry.wakeup.take()?;
1708 Some(detach_provider_handle(
1709 identity,
1710 entry.claim_generation,
1711 owned,
1712 ))
1713 }
1714
1715 pub(crate) fn take_work_handle(&mut self, identity: &TimerIdentity) -> Option<ProviderHandle> {
1716 let entry = self.entries.get_mut(identity)?;
1717 let owned = entry.work.take()?;
1718 Some(detach_provider_handle(
1719 identity,
1720 entry.claim_generation,
1721 owned,
1722 ))
1723 }
1724
1725 pub(crate) fn take_provider_handles_for_claim(
1726 &mut self,
1727 claim: &RegistrationClaim,
1728 ) -> Result<ProviderHandles, RegistryError> {
1729 let identity = claim.identity.clone();
1730 let entry = self.entry_mut(claim)?;
1731 Ok(ProviderHandles {
1732 wakeup: entry
1733 .wakeup
1734 .take()
1735 .map(|owned| detach_provider_handle(&identity, entry.claim_generation, owned)),
1736 work: entry
1737 .work
1738 .take()
1739 .map(|owned| detach_provider_handle(&identity, entry.claim_generation, owned)),
1740 })
1741 }
1742
1743 pub(crate) fn consume_provider_handle(&mut self, token: &CallbackToken) {
1744 let Some(entry) = self.entries.get_mut(token.identity()) else {
1745 return;
1746 };
1747 let slot = match token.role {
1748 CallbackRole::OrdinaryWork | CallbackRole::WatchdogScheduler => &mut entry.wakeup,
1749 CallbackRole::WatchdogWork => &mut entry.work,
1750 };
1751 let matches_token = slot.as_ref().is_some_and(|owned| {
1752 owned.callback_generation == token.callback_generation && owned.role == token.role
1753 });
1754 if matches_token {
1755 *slot = None;
1756 }
1757 }
1758
1759 pub(crate) fn confirm_effect_applied(
1765 &mut self,
1766 effect: &RegistryEffect,
1767 ) -> Result<(), RegistryError> {
1768 match effect {
1769 RegistryEffect::None | RegistryEffect::ClearCallbacks { .. } => Ok(()),
1770 RegistryEffect::ArmWakeup {
1771 token, delay_ns, ..
1772 } => {
1773 let entry = self.entry_by_token_mut(token, token.role)?;
1774 let valid_generation = match (&entry.control, token.role) {
1775 (EntryControl::Ordinary { control, .. }, CallbackRole::OrdinaryWork) => {
1776 matches!(
1777 control.registration(),
1778 crate::TimerRegistration::Scheduled { generation, .. }
1779 if generation == token.callback_generation
1780 )
1781 }
1782 (EntryControl::Watchdog(control), CallbackRole::WatchdogScheduler) => matches!(
1783 control.state,
1784 WatchdogState::Scheduled {
1785 scheduler_generation,
1786 ..
1787 } if scheduler_generation == token.callback_generation
1788 ),
1789 (EntryControl::Ordinary { .. } | EntryControl::Watchdog(_), _) => false,
1790 };
1791 if !valid_generation {
1792 return Err(RegistryError::StaleCallback);
1793 }
1794 if entry.confirmed_wakeup_generation == Some(token.callback_generation) {
1795 return Ok(());
1796 }
1797 entry.confirmed_wakeup_generation = Some(token.callback_generation);
1798 entry.latest_armed_delay_ns = Some(*delay_ns);
1799 entry.observability.counters_mut().record_wakeup_armed();
1800 Ok(())
1801 }
1802 RegistryEffect::DispatchWatchdog {
1803 successor,
1804 successor_delay_ns,
1805 work,
1806 ..
1807 } => {
1808 let successor_claim_generation = successor.claim_generation;
1809 let successor_callback_generation = successor.callback_generation;
1810 let successor_role = successor.role;
1811 let entry = self.entry_by_token_mut(successor, successor_role)?;
1812 if successor_role != CallbackRole::WatchdogScheduler
1813 || work.role != CallbackRole::WatchdogWork
1814 || work.claim_generation != successor_claim_generation
1815 {
1816 return Err(RegistryError::StaleCallback);
1817 }
1818 let EntryControl::Watchdog(control) = &entry.control else {
1819 return Err(RegistryError::StaleCallback);
1820 };
1821 if !matches!(
1822 control.state,
1823 WatchdogState::AwaitingWork {
1824 successor_generation,
1825 attempt_generation,
1826 ..
1827 } if successor_generation == successor_callback_generation
1828 && attempt_generation == work.callback_generation
1829 ) {
1830 return Err(RegistryError::StaleCallback);
1831 }
1832 if entry.confirmed_wakeup_generation == Some(successor_callback_generation)
1833 && entry.confirmed_work_generation == Some(work.callback_generation)
1834 {
1835 return Ok(());
1836 }
1837 entry.confirmed_wakeup_generation = Some(successor_callback_generation);
1838 entry.confirmed_work_generation = Some(work.callback_generation);
1839 entry.latest_armed_delay_ns = Some(*successor_delay_ns);
1840 entry.observability.counters_mut().record_wakeup_armed();
1841 entry.observability.counters_mut().record_work_dispatched();
1842 Ok(())
1843 }
1844 }
1845 }
1846
1847 fn entry(&self, claim: &RegistrationClaim) -> Result<&Entry, RegistryError> {
1848 let entry = self
1849 .entries
1850 .get(claim.identity())
1851 .ok_or(RegistryError::UnknownRegistration)?;
1852 if entry.claim_generation != claim.claim_generation() {
1853 return Err(RegistryError::StaleRegistration);
1854 }
1855 Ok(entry)
1856 }
1857
1858 fn entry_mut(&mut self, claim: &RegistrationClaim) -> Result<&mut Entry, RegistryError> {
1859 let entry = self
1860 .entries
1861 .get_mut(claim.identity())
1862 .ok_or(RegistryError::UnknownRegistration)?;
1863 if entry.claim_generation != claim.claim_generation() {
1864 return Err(RegistryError::StaleRegistration);
1865 }
1866 Ok(entry)
1867 }
1868
1869 fn entry_by_token_mut(
1870 &mut self,
1871 token: &CallbackToken,
1872 role: CallbackRole,
1873 ) -> Result<&mut Entry, RegistryError> {
1874 let entry = self
1875 .entries
1876 .get_mut(token.identity())
1877 .ok_or(RegistryError::StaleCallback)?;
1878 if entry.claim_generation != token.claim_generation || token.role != role {
1879 return Err(RegistryError::StaleCallback);
1880 }
1881 Ok(entry)
1882 }
1883
1884 fn running_work_entry(&self, token: &CallbackToken) -> Result<&Entry, RegistryError> {
1885 let entry = self
1886 .entries
1887 .get(token.identity())
1888 .ok_or(RegistryError::StaleCallback)?;
1889 if token.claim_generation != entry.claim_generation {
1890 return Err(RegistryError::StaleCallback);
1891 }
1892 let active = match (&entry.control, token.role) {
1893 (EntryControl::Ordinary { control, .. }, CallbackRole::OrdinaryWork) => matches!(
1894 control.registration(),
1895 crate::TimerRegistration::Running { generation }
1896 if generation == token.callback_generation
1897 ),
1898 (EntryControl::Watchdog(control), CallbackRole::WatchdogWork) => matches!(
1899 control.state,
1900 WatchdogState::AwaitingWork {
1901 attempt_generation,
1902 attempt_status: WatchdogAttemptStatus::Running,
1903 ..
1904 } if attempt_generation == token.callback_generation
1905 ),
1906 (
1907 EntryControl::Ordinary { .. },
1908 CallbackRole::WatchdogScheduler | CallbackRole::WatchdogWork,
1909 )
1910 | (
1911 EntryControl::Watchdog(_),
1912 CallbackRole::OrdinaryWork | CallbackRole::WatchdogScheduler,
1913 ) => false,
1914 };
1915 if active {
1916 Ok(entry)
1917 } else {
1918 Err(RegistryError::StaleCallback)
1919 }
1920 }
1921}
1922
1923fn token_for(
1924 claim: &RegistrationClaim,
1925 callback_generation: u64,
1926 role: CallbackRole,
1927) -> CallbackToken {
1928 CallbackToken::new(
1929 claim.identity.clone(),
1930 claim.claim_generation,
1931 callback_generation,
1932 role,
1933 )
1934}
1935
1936fn detach_provider_handle(
1937 identity: &TimerIdentity,
1938 claim_generation: u64,
1939 owned: OwnedProviderHandle,
1940) -> ProviderHandle {
1941 ProviderHandle {
1942 token: CallbackToken::new(
1943 identity.clone(),
1944 claim_generation,
1945 owned.callback_generation,
1946 owned.role,
1947 ),
1948 handle: owned.handle,
1949 }
1950}
1951
1952const fn clear_callbacks(
1953 identity: TimerIdentity,
1954 clear_wakeup: bool,
1955 clear_work: bool,
1956) -> RegistryEffect {
1957 RegistryEffect::ClearCallbacks {
1958 identity,
1959 clear_wakeup,
1960 clear_work,
1961 }
1962}
1963
1964fn apply_ordinary_action(
1965 entry: &mut Entry,
1966 identity: TimerIdentity,
1967 now_ns: u64,
1968 action: TimerControlAction,
1969 requested: PendingSchedule,
1970) -> RegistryTransition {
1971 match action {
1972 TimerControlAction::Arm {
1973 generation,
1974 deadline_ns,
1975 }
1976 | TimerControlAction::Replace {
1977 generation,
1978 deadline_ns,
1979 } => {
1980 let replace = matches!(action, TimerControlAction::Replace { .. });
1981 entry.scheduling_mode = requested.mode;
1982 RegistryTransition::normal(RegistryEffect::ArmWakeup {
1983 token: CallbackToken::new(
1984 identity,
1985 entry.claim_generation,
1986 generation,
1987 CallbackRole::OrdinaryWork,
1988 ),
1989 deadline_ns,
1990 delay_ns: deadline_ns.saturating_sub(now_ns),
1991 replace,
1992 })
1993 }
1994 TimerControlAction::None => {
1995 entry.observability.counters_mut().record_coalesced();
1996 RegistryTransition::normal(RegistryEffect::None)
1997 }
1998 TimerControlAction::Clear | TimerControlAction::Disarm { .. } => {
1999 terminal_ordinary(entry, identity, TimerControlError::StaleCompletion)
2000 }
2001 }
2002}
2003
2004fn terminal_ordinary(
2005 entry: &mut Entry,
2006 identity: TimerIdentity,
2007 error: TimerControlError,
2008) -> RegistryTransition {
2009 let failure = map_control_failure(error);
2010 let EntryControl::Ordinary {
2011 control,
2012 pending,
2013 inactive_reason,
2014 } = &mut entry.control
2015 else {
2016 return RegistryTransition::terminal(RegistryEffect::None, failure);
2017 };
2018 let clear_wakeup = control.terminate();
2019 *pending = None;
2020 *inactive_reason = InactiveReason::ControlFailure(failure);
2021 RegistryTransition::terminal(
2022 RegistryEffect::ClearCallbacks {
2023 identity,
2024 clear_wakeup,
2025 clear_work: false,
2026 },
2027 failure,
2028 )
2029}
2030
2031fn terminal_completion(
2032 entry: &mut Entry,
2033 generation: u64,
2034 work_count: u64,
2035 now_ns: u64,
2036 failure: TimerControlFailure,
2037) -> RegistryTransition {
2038 let EntryControl::Ordinary {
2039 control,
2040 pending,
2041 inactive_reason,
2042 } = &mut entry.control
2043 else {
2044 return RegistryTransition::terminal(RegistryEffect::None, failure);
2045 };
2046 if control.registration() == (crate::TimerRegistration::Running { generation }) {
2047 control.terminate();
2048 }
2049 *pending = None;
2050 *inactive_reason = InactiveReason::ControlFailure(failure);
2051 entry.latest_directive = Some(TimerDirectiveSnapshot::Stop);
2052 entry
2053 .observability
2054 .record_completion(TimerCompletion::invariant_failure(work_count), now_ns);
2055 RegistryTransition::terminal(RegistryEffect::None, failure)
2056}
2057
2058fn invariant_completion(
2059 entry: &mut Entry,
2060 generation: u64,
2061 completion: TimerCompletion,
2062 now_ns: u64,
2063) -> RegistryTransition {
2064 let EntryControl::Ordinary {
2065 control,
2066 pending,
2067 inactive_reason,
2068 } = &mut entry.control
2069 else {
2070 return RegistryTransition::normal(RegistryEffect::None);
2071 };
2072 if control.registration() == (crate::TimerRegistration::Running { generation }) {
2073 control.terminate();
2074 }
2075 *pending = None;
2076 *inactive_reason = InactiveReason::InvariantFailure;
2077 entry.latest_directive = Some(TimerDirectiveSnapshot::Stop);
2078 entry.observability.record_completion(completion, now_ns);
2079 RegistryTransition::normal(RegistryEffect::None)
2080}
2081
2082const fn map_control_failure(error: TimerControlError) -> TimerControlFailure {
2083 match error {
2084 TimerControlError::RequestSequenceExhausted => {
2085 TimerControlFailure::RequestSequenceExhausted
2086 }
2087 TimerControlError::GenerationExhausted => TimerControlFailure::GenerationExhausted,
2088 TimerControlError::StaleCompletion => TimerControlFailure::DirectiveNotAllowed,
2089 }
2090}
2091
2092const fn map_schedule_failure(error: ScheduleError) -> TimerControlFailure {
2093 match error {
2094 ScheduleError::DeadlineOverflow => TimerControlFailure::DeadlineOverflow,
2095 ScheduleError::DelayOutOfRange => TimerControlFailure::DelayOutOfRange,
2096 ScheduleError::ZeroCadence | ScheduleError::MissingCadence => {
2097 TimerControlFailure::DirectiveNotAllowed
2098 }
2099 }
2100}
2101
2102const fn select_completion_schedule(
2103 pending: Option<OrdinaryPending>,
2104 callback: Option<PendingSchedule>,
2105) -> Option<PendingSchedule> {
2106 match pending {
2107 Some(OrdinaryPending::Cancel | OrdinaryPending::Unregister) => None,
2108 Some(OrdinaryPending::Reconcile(pending)) => Some(pending),
2109 Some(OrdinaryPending::Schedule(pending)) => match callback {
2110 Some(callback) if callback.deadline_ns < pending.deadline_ns => Some(callback),
2111 Some(_) | None => Some(pending),
2112 },
2113 None => callback,
2114 }
2115}
2116
2117const fn select_pending_ordinary(
2118 current: Option<OrdinaryPending>,
2119 request: OrdinaryRequest,
2120 requested: PendingSchedule,
2121) -> OrdinaryPending {
2122 if matches!(current, Some(OrdinaryPending::Unregister)) {
2123 return OrdinaryPending::Unregister;
2124 }
2125 match request {
2126 OrdinaryRequest::Reconcile => OrdinaryPending::Reconcile(requested),
2127 OrdinaryRequest::Ensure => match current {
2128 Some(OrdinaryPending::Reconcile(current) | OrdinaryPending::Schedule(current))
2129 if current.deadline_ns <= requested.deadline_ns =>
2130 {
2131 OrdinaryPending::Schedule(current)
2132 }
2133 Some(
2134 OrdinaryPending::Cancel
2135 | OrdinaryPending::Reconcile(_)
2136 | OrdinaryPending::Schedule(_),
2137 )
2138 | None => OrdinaryPending::Schedule(requested),
2139 Some(OrdinaryPending::Unregister) => OrdinaryPending::Unregister,
2140 },
2141 }
2142}
2143
2144fn cancel_watchdog(
2145 control: &mut WatchdogControl,
2146 identity: &TimerIdentity,
2147) -> (RegistryTransition, bool) {
2148 match control.state {
2149 WatchdogState::Inactive => (RegistryTransition::normal(RegistryEffect::None), false),
2150 WatchdogState::AwaitingWork {
2151 attempt_status: WatchdogAttemptStatus::Running,
2152 ..
2153 } => {
2154 let Some(sequence) = control.request_sequence.checked_add(1) else {
2155 control.state = WatchdogState::Inactive;
2156 control.inactive_reason =
2157 InactiveReason::ControlFailure(TimerControlFailure::RequestSequenceExhausted);
2158 return (
2159 RegistryTransition::terminal(
2160 clear_callbacks(identity.clone(), true, false),
2161 TimerControlFailure::RequestSequenceExhausted,
2162 ),
2163 true,
2164 );
2165 };
2166 control.request_sequence = sequence;
2167 if !matches!(control.pending, Some(WatchdogPending::Unregister)) {
2168 control.pending = Some(WatchdogPending::Cancel);
2169 }
2170 (RegistryTransition::normal(RegistryEffect::None), false)
2171 }
2172 WatchdogState::Scheduled { .. }
2173 | WatchdogState::AwaitingWork {
2174 attempt_status: WatchdogAttemptStatus::Dispatched,
2175 ..
2176 } => {
2177 let clear_work = matches!(control.state, WatchdogState::AwaitingWork { .. });
2178 let Some(scheduler_generation) = control.scheduler_generation.checked_add(1) else {
2179 control.state = WatchdogState::Inactive;
2180 control.inactive_reason =
2181 InactiveReason::ControlFailure(TimerControlFailure::GenerationExhausted);
2182 return (
2183 RegistryTransition::terminal(
2184 clear_callbacks(identity.clone(), true, clear_work),
2185 TimerControlFailure::GenerationExhausted,
2186 ),
2187 true,
2188 );
2189 };
2190 let Some(attempt_generation) = control.attempt_generation.checked_add(1) else {
2191 control.state = WatchdogState::Inactive;
2192 control.inactive_reason =
2193 InactiveReason::ControlFailure(TimerControlFailure::GenerationExhausted);
2194 return (
2195 RegistryTransition::terminal(
2196 clear_callbacks(identity.clone(), true, clear_work),
2197 TimerControlFailure::GenerationExhausted,
2198 ),
2199 true,
2200 );
2201 };
2202 control.scheduler_generation = scheduler_generation;
2203 control.attempt_generation = attempt_generation;
2204 control.state = WatchdogState::Inactive;
2205 control.pending = None;
2206 control.inactive_reason = InactiveReason::Cancelled;
2207 (
2208 RegistryTransition::normal(clear_callbacks(identity.clone(), true, clear_work)),
2209 true,
2210 )
2211 }
2212 }
2213}
2214
2215fn remove_after(
2216 entries: &mut BTreeMap<TimerIdentity, Entry>,
2217 identity: &TimerIdentity,
2218 transition: RegistryTransition,
2219 remove: bool,
2220) -> RegistryTransition {
2221 if remove {
2222 entries.remove(identity);
2223 }
2224 transition
2225}
2226
2227#[cfg(test)]
2228mod tests;