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