hrx/execution/
completion.rs1use crate::{Error, Result};
2use std::{
3 future::Future,
4 pin::Pin,
5 sync::{
6 Arc, Condvar, Mutex,
7 atomic::{AtomicBool, Ordering},
8 },
9 task::{Context, Poll, Waker},
10 time::{Duration, Instant},
11};
12
13pub(super) struct State {
14 submitted: Option<Instant>,
15 started: Option<Instant>,
16 finished: Option<Instant>,
17 pub done: bool,
18 pub failure: Option<Arc<Error>>,
19 pub cancelled: bool,
20 wakers: [Option<Waker>; 4],
21 overflow_wakers: Vec<Waker>,
22}
23pub(super) struct Signal {
24 pub state: Mutex<State>,
25 changed: Condvar,
26 pub cancel: AtomicBool,
27}
28impl Signal {
29 pub fn new() -> Arc<Self> {
30 Arc::new(Self {
31 state: Mutex::new(State {
32 submitted: None,
33 started: None,
34 finished: None,
35 done: true,
36 failure: None,
37 cancelled: false,
38 wakers: std::array::from_fn(|_| None),
39 overflow_wakers: Vec::new(),
40 }),
41 changed: Condvar::new(),
42 cancel: AtomicBool::new(false),
43 })
44 }
45 pub fn reset(&self) {
46 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
47 state.done = false;
48 state.submitted = Some(Instant::now());
49 state.started = None;
50 state.finished = None;
51 state.failure = None;
52 state.cancelled = false;
53 state.wakers.fill(None);
54 state.overflow_wakers.clear();
55 self.cancel.store(false, Ordering::Release);
56 }
57 pub fn finish(&self, failure: Option<Arc<Error>>) {
58 let (wakers, overflow) = {
59 let mut state = self.state.lock().unwrap_or_else(|e| e.into_inner());
60 state.failure = failure;
61 state.cancelled = self.cancel.load(Ordering::Acquire);
62 state.done = true;
63 state.finished = Some(Instant::now());
64 (
65 std::mem::replace(&mut state.wakers, std::array::from_fn(|_| None)),
66 std::mem::take(&mut state.overflow_wakers),
67 )
68 };
69 self.changed.notify_all();
70 for waker in wakers.into_iter().flatten().chain(overflow) {
71 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| waker.wake()));
73 }
74 }
75}
76impl Signal {
77 pub fn started(&self, now: Instant) {
78 self.state
79 .lock()
80 .unwrap_or_else(|e| e.into_inner())
81 .started
82 .get_or_insert(now);
83 }
84}
85
86#[derive(Clone, Copy, Debug, serde::Serialize)]
89pub struct CompletionProfile {
90 pub elapsed_us: f64,
92 pub queue_us: Option<f64>,
94}
95
96fn outcome(state: &State) -> Result<()> {
97 if let Some(message) = &state.failure {
98 return Err(Error::Execution {
99 source: message.clone(),
100 });
101 }
102 if state.cancelled {
103 return Err(Error::Cancelled);
104 }
105 Ok(())
106}
107#[derive(Clone)]
111pub struct Completion {
112 pub(super) signal: Arc<Signal>,
113 pub(super) core: std::sync::Weak<super::scheduler::Core>,
114}
115impl Completion {
116 pub fn finished_at(&self) -> Option<Instant> {
120 self.signal
121 .state
122 .lock()
123 .unwrap_or_else(|e| e.into_inner())
124 .finished
125 }
126 pub fn profile(&self) -> Option<CompletionProfile> {
128 let state = self.signal.state.lock().unwrap_or_else(|e| e.into_inner());
129 let submitted = state.submitted?;
130 Some(CompletionProfile {
131 elapsed_us: state.finished?.duration_since(submitted).as_secs_f64() * 1e6,
132 queue_us: state
133 .started
134 .map(|start| start.duration_since(submitted).as_secs_f64() * 1e6),
135 })
136 }
137 pub(crate) fn validate_runtime(&self, runtime: &super::Runtime) -> Result<()> {
138 if let Some(owner) = self.core.upgrade() {
139 if !Arc::ptr_eq(&runtime.inner.core, &owner) {
140 return Err(Error::Message(
141 "completion belongs to another runtime".into(),
142 ));
143 }
144 } else if !self.is_complete() {
145 return Err(Error::Message("orphaned incomplete producer".into()));
146 }
147 Ok(())
148 }
149 pub fn result(&self) -> Option<Result<()>> {
151 let state = self.signal.state.lock().unwrap_or_else(|e| e.into_inner());
152 state.done.then(|| outcome(&state))
153 }
154
155 pub(crate) fn ready() -> Self {
156 Self {
157 signal: Signal::new(),
158 core: std::sync::Weak::new(),
159 }
160 }
161
162 pub fn is_complete(&self) -> bool {
164 self.signal
165 .state
166 .lock()
167 .unwrap_or_else(|e| e.into_inner())
168 .done
169 }
170 pub fn wait(&self) -> Result<()> {
174 if let Some(core) = self.core.upgrade() {
175 super::scheduler::help(&core, &self.signal);
176 }
177 let mut state = self.signal.state.lock().unwrap_or_else(|e| e.into_inner());
178 while !state.done {
179 state = self
180 .signal
181 .changed
182 .wait(state)
183 .unwrap_or_else(|e| e.into_inner());
184 }
185 outcome(&state)
186 }
187 pub fn wait_timeout(&self, timeout: Duration) -> Result<bool> {
189 let start = Instant::now();
190 let mut state = self.signal.state.lock().unwrap_or_else(|e| e.into_inner());
191 while !state.done {
192 let Some(left) = timeout.checked_sub(start.elapsed()) else {
193 return Ok(false);
194 };
195 let (next, _) = self
196 .signal
197 .changed
198 .wait_timeout(state, left)
199 .unwrap_or_else(|e| e.into_inner());
200 state = next;
201 }
202 outcome(&state)?;
203 Ok(true)
204 }
205 pub fn cancel(&self) {
208 self.signal.cancel.store(true, Ordering::Release);
209 if let Some(core) = self.core.upgrade() {
210 core.changed.notify_all();
211 }
212 }
213}
214impl Future for Completion {
215 type Output = Result<()>;
216 fn poll(self: Pin<&mut Self>, context: &mut Context<'_>) -> Poll<Self::Output> {
217 let mut state = self.signal.state.lock().unwrap_or_else(|e| e.into_inner());
218 if state.done {
219 return Poll::Ready(outcome(&state));
220 }
221 if !state
222 .wakers
223 .iter()
224 .flatten()
225 .chain(&state.overflow_wakers)
226 .any(|w| w.will_wake(context.waker()))
227 {
228 if let Some(slot) = state.wakers.iter_mut().find(|slot| slot.is_none()) {
229 *slot = Some(context.waker().clone());
230 } else {
231 state.overflow_wakers.push(context.waker().clone());
232 }
233 }
234 Poll::Pending
235 }
236}
237#[cfg(test)]
238mod tests {
239 use super::*;
240 #[test]
241 fn timeout_does_not_cancel_and_detach_retains_state() {
242 let signal = Signal::new();
243 signal.reset();
244 let completion = Completion {
245 signal: signal.clone(),
246 core: std::sync::Weak::new(),
247 };
248 assert!(!completion.wait_timeout(Duration::ZERO).unwrap());
249 assert!(!signal.cancel.load(Ordering::Acquire));
250 drop(completion);
251 signal.finish(None);
252 Completion {
253 signal,
254 core: std::sync::Weak::new(),
255 }
256 .wait()
257 .unwrap();
258 }
259 #[test]
260 fn future_wakeup_and_error() {
261 use std::task::Wake;
262 struct Counter(std::sync::atomic::AtomicUsize);
263 impl Wake for Counter {
264 fn wake(self: Arc<Self>) {
265 self.0.fetch_add(1, Ordering::Relaxed);
266 }
267 }
268 let counter = Arc::new(Counter(std::sync::atomic::AtomicUsize::new(0)));
269 let waker = Waker::from(counter.clone());
270 let mut context = Context::from_waker(&waker);
271 let signal = Signal::new();
272 signal.reset();
273 let mut completion = Completion {
274 signal: signal.clone(),
275 core: std::sync::Weak::new(),
276 };
277 assert!(Pin::new(&mut completion).poll(&mut context).is_pending());
278 signal.finish(Some(Arc::new(Error::DeviceLost(
279 "injected native failure".into(),
280 ))));
281 assert_eq!(counter.0.load(Ordering::Relaxed), 1);
282 assert!(matches!(
283 Pin::new(&mut completion).poll(&mut context),
284 Poll::Ready(Err(Error::Execution { .. }))
285 ));
286 }
287}