Skip to main content

ax_task/
interrupt.rs

1//! Task interruption publication state.
2
3use core::sync::atomic::{AtomicU64, Ordering};
4
5/// A task interruption observation captured before an owner-side safe-point scan.
6///
7/// Obtain a snapshot through [`crate::TaskInner::interrupt_snapshot`] and pass it
8/// to [`crate::TaskInner::acknowledge_interrupt`] after that scan completes.
9#[derive(Debug, Clone, Copy)]
10pub struct InterruptSnapshot(u64);
11
12/// Sticky interruption publication shared by remote producers and one task.
13pub(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}