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