vmi_core/handler/
termination.rs1use std::{
2 ops::ControlFlow,
3 sync::{
4 Arc,
5 atomic::{AtomicBool, Ordering},
6 },
7 time::{Duration, Instant},
8};
9
10use crate::{VmiContext, VmiEventResponse, VmiHandler, VmiOs, VmiSession};
11
12enum TerminationState {
14 Running,
16
17 Interrupted(Instant),
22
23 Terminated,
26}
27
28pub struct VmiTerminationHandler<Handler> {
45 handler: Handler,
47
48 deadline: Option<Instant>,
50
51 termination_flag: Option<Arc<AtomicBool>>,
53
54 termination_timeout: Option<Duration>,
56
57 state: TerminationState,
59}
60
61impl<Handler> VmiTerminationHandler<Handler> {
62 pub fn new(handler: Handler) -> Self {
64 Self {
65 handler,
66 deadline: None,
67 termination_flag: None,
68 termination_timeout: None,
69 state: TerminationState::Running,
70 }
71 }
72
73 pub fn with_deadline(self, deadline: Instant) -> Self {
75 Self {
76 deadline: Some(deadline),
77 ..self
78 }
79 }
80
81 pub fn with_termination_flag(self, termination_flag: Arc<AtomicBool>) -> Self {
83 Self {
84 termination_flag: Some(termination_flag),
85 ..self
86 }
87 }
88
89 pub fn with_termination_timeout(self, termination_timeout: Duration) -> Self {
93 Self {
94 termination_timeout: Some(termination_timeout),
95 ..self
96 }
97 }
98
99 fn check_termination<Os>(&mut self, session: &VmiSession<Os>)
102 where
103 Os: VmiOs,
104 Handler: VmiHandler<Os>,
105 {
106 if !matches!(self.state, TerminationState::Running) {
107 return;
108 }
109
110 let timed_out = self
111 .deadline
112 .is_some_and(|deadline| Instant::now() >= deadline);
113
114 let terminated = self
115 .termination_flag
116 .as_ref()
117 .is_some_and(|termination_flag| termination_flag.load(Ordering::Relaxed));
118
119 if timed_out || terminated {
120 self.interrupt(session);
121 }
122 }
123
124 fn interrupt<Os>(&mut self, session: &VmiSession<Os>)
127 where
128 Os: VmiOs,
129 Handler: VmiHandler<Os>,
130 {
131 self.state = match self.state {
132 TerminationState::Running => match self.handler.handle_interrupted(session) {
133 ControlFlow::Break(()) => TerminationState::Terminated,
134 ControlFlow::Continue(()) => TerminationState::Interrupted(Instant::now()),
135 },
136 TerminationState::Interrupted(_) | TerminationState::Terminated => {
137 TerminationState::Terminated
138 }
139 };
140 }
141}
142
143impl<Os, Handler> VmiHandler<Os> for VmiTerminationHandler<Handler>
144where
145 Os: VmiOs,
146 Handler: VmiHandler<Os>,
147{
148 type Output = Option<Handler::Output>;
149
150 fn handle_event(&mut self, vmi: VmiContext<Os>) -> VmiEventResponse<Os::Architecture> {
151 self.check_termination(&vmi);
152 self.handler.handle_event(vmi)
153 }
154
155 fn handle_timeout(&mut self, session: &VmiSession<Os>) {
156 self.check_termination(session);
157 self.handler.handle_timeout(session);
158 }
159
160 fn handle_interrupted(&mut self, session: &VmiSession<Os>) -> ControlFlow<()> {
161 self.interrupt(session);
162
163 match self.state {
164 TerminationState::Interrupted(_) => ControlFlow::Continue(()),
165 TerminationState::Running | TerminationState::Terminated => ControlFlow::Break(()),
166 }
167 }
168
169 fn cleanup(&mut self, session: &VmiSession<Os>) {
170 self.handler.cleanup(session);
171 }
172
173 fn poll(&mut self) -> Option<Self::Output> {
174 if let Some(output) = self.handler.poll() {
175 return Some(Some(output));
176 }
177
178 match self.state {
179 TerminationState::Running => None,
180 TerminationState::Interrupted(interrupted_at) => {
181 let termination_timeout = self.termination_timeout?;
182 if interrupted_at.elapsed() < termination_timeout {
183 return None;
184 }
185
186 tracing::warn!("handler did not finish in time");
187 Some(None)
188 }
189 TerminationState::Terminated => Some(None),
190 }
191 }
192}