Skip to main content

deepstrike_core/signals/
attention.rs

1use crate::scheduler::tcb::TaskLifecycle;
2use crate::types::policy::SignalDisposition;
3use crate::types::signal::{RuntimeSignal, Urgency};
4
5/// Default attention policy based on signal urgency.
6pub struct UrgencyBasedPolicy;
7
8impl UrgencyBasedPolicy {
9    pub fn evaluate(&self, signal: &RuntimeSignal, lifecycle: TaskLifecycle) -> SignalDisposition {
10        if lifecycle.is_terminal() {
11            return SignalDisposition::Queue;
12        }
13
14        match signal.urgency {
15            Urgency::Critical => {
16                if matches!(lifecycle, TaskLifecycle::Running | TaskLifecycle::Suspended) {
17                    SignalDisposition::InterruptNow
18                } else {
19                    SignalDisposition::Run
20                }
21            }
22            Urgency::High => {
23                if matches!(lifecycle, TaskLifecycle::Running | TaskLifecycle::Suspended) {
24                    SignalDisposition::Interrupt
25                } else {
26                    SignalDisposition::Run
27                }
28            }
29            Urgency::Normal => {
30                if matches!(lifecycle, TaskLifecycle::Ready) {
31                    SignalDisposition::Run
32                } else {
33                    SignalDisposition::Queue
34                }
35            }
36            Urgency::Low => SignalDisposition::Observe,
37        }
38    }
39}
40
41#[cfg(test)]
42mod tests {
43    use super::*;
44    use crate::scheduler::tcb::TaskLifecycle;
45    use crate::types::result::TerminationReason;
46    use crate::types::signal::{SignalSource, SignalType};
47
48    #[test]
49    fn critical_signal_interrupts_running() {
50        let policy = UrgencyBasedPolicy;
51        let signal = RuntimeSignal::new(
52            SignalSource::Gateway,
53            SignalType::Alert,
54            Urgency::Critical,
55            "fire",
56        );
57        assert_eq!(
58            policy.evaluate(&signal, TaskLifecycle::Running),
59            SignalDisposition::InterruptNow
60        );
61    }
62
63    #[test]
64    fn low_signal_observed() {
65        let policy = UrgencyBasedPolicy;
66        let signal =
67            RuntimeSignal::new(SignalSource::Cron, SignalType::Event, Urgency::Low, "tick");
68        assert_eq!(
69            policy.evaluate(&signal, TaskLifecycle::Ready),
70            SignalDisposition::Observe
71        );
72    }
73
74    #[test]
75    fn normal_signal_runs_an_idle_nonterminal_task() {
76        let policy = UrgencyBasedPolicy;
77        let signal = RuntimeSignal::new(
78            SignalSource::Cron,
79            SignalType::Job,
80            Urgency::Normal,
81            "work ready",
82        );
83
84        assert_eq!(
85            policy.evaluate(&signal, TaskLifecycle::Ready),
86            SignalDisposition::Run
87        );
88        assert_eq!(
89            policy.evaluate(&signal, TaskLifecycle::Running),
90            SignalDisposition::Queue
91        );
92    }
93
94    #[test]
95    fn terminal_task_queues_signal_for_host_decision() {
96        let policy = UrgencyBasedPolicy;
97        let signal = RuntimeSignal::new(
98            SignalSource::Gateway,
99            SignalType::Alert,
100            Urgency::Critical,
101            "late signal",
102        );
103
104        assert_eq!(
105            policy.evaluate(&signal, TaskLifecycle::Done(TerminationReason::Completed),),
106            SignalDisposition::Queue
107        );
108    }
109}