1use core::sync::atomic::{AtomicU64, Ordering};
4
5#[derive(Debug, Clone, Copy)]
10pub struct InterruptSnapshot(u64);
11
12pub(crate) struct InterruptState {
14 published: AtomicU64,
15 acknowledged: AtomicU64,
16}
17
18impl InterruptState {
19 pub(crate) const fn new() -> Self {
20 Self {
21 published: AtomicU64::new(0),
22 acknowledged: AtomicU64::new(0),
23 }
24 }
25
26 pub(crate) fn publish(&self) {
27 self.published
28 .try_update(Ordering::Release, Ordering::Relaxed, |generation| {
29 generation.checked_add(1)
30 })
31 .expect("task interruption generation exhausted");
32 }
33
34 pub(crate) fn consume(&self) -> bool {
35 self.acknowledge(self.snapshot())
36 }
37
38 pub(crate) fn is_pending(&self) -> bool {
39 self.published.load(Ordering::Acquire) > self.acknowledged.load(Ordering::Acquire)
40 }
41
42 pub(crate) fn snapshot(&self) -> InterruptSnapshot {
43 InterruptSnapshot(self.published.load(Ordering::Acquire))
44 }
45
46 pub(crate) fn acknowledge(&self, snapshot: InterruptSnapshot) -> bool {
47 let mut acknowledged = self.acknowledged.load(Ordering::Acquire);
48 while acknowledged < snapshot.0 {
49 match self.acknowledged.compare_exchange_weak(
50 acknowledged,
51 snapshot.0,
52 Ordering::AcqRel,
53 Ordering::Acquire,
54 ) {
55 Ok(_) => return true,
56 Err(current) => acknowledged = current,
57 }
58 }
59 false
60 }
61}
62
63#[cfg(test)]
64mod tests {
65 use super::InterruptState;
66
67 #[test]
68 fn publication_after_snapshot_survives_acknowledgement() {
69 let state = InterruptState::new();
70 state.publish();
71 let scanned = state.snapshot();
72
73 state.publish();
74 let _advanced = state.acknowledge(scanned);
75
76 assert!(
77 state.is_pending(),
78 "acknowledging an older scan must not erase a later publication"
79 );
80 }
81
82 #[test]
83 fn snapshot_acknowledges_only_visible_publications() {
84 let state = InterruptState::new();
85 state.publish();
86
87 let scanned = state.snapshot();
88
89 assert!(state.acknowledge(scanned));
90 assert!(!state.is_pending());
91 }
92
93 #[test]
94 fn older_acknowledgement_cannot_regress_a_consumer() {
95 let state = InterruptState::new();
96 let old = state.snapshot();
97 state.publish();
98
99 assert!(state.consume());
100 assert!(!state.acknowledge(old));
101 assert!(!state.is_pending());
102 }
103}