videre_core/
io_timeout.rs1use std::sync::mpsc;
2use std::thread;
3use std::time::{Duration, Instant};
4
5pub const DEFAULT_IO_TIMEOUT: Duration = Duration::from_secs(20);
11
12pub struct TimedOut;
17
18pub fn run_with_timeout<T, F>(timeout: Duration, f: F) -> Result<T, TimedOut>
23where
24 F: FnOnce() -> T + Send + 'static,
25 T: Send + 'static,
26{
27 let (tx, rx) = mpsc::channel();
28 thread::spawn(move || {
29 let _ = tx.send(f());
30 });
31 rx.recv_timeout(timeout).map_err(|_| TimedOut)
32}
33
34#[derive(Debug, PartialEq, Eq)]
36pub enum WaitOutcome {
37 Success,
38 Failed,
39 TimedOut,
40}
41
42pub fn wait_with_timeout(child: &mut std::process::Child, timeout: Duration) -> WaitOutcome {
47 let start = Instant::now();
48 loop {
49 match child.try_wait() {
50 Ok(Some(status)) => {
51 return if status.success() { WaitOutcome::Success } else { WaitOutcome::Failed };
52 }
53 Ok(None) => {
54 if start.elapsed() >= timeout {
55 let _ = child.kill();
56 let _ = child.wait();
57 return WaitOutcome::TimedOut;
58 }
59 thread::sleep(Duration::from_millis(50));
60 }
61 Err(_) => return WaitOutcome::Failed,
62 }
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn returns_ok_when_operation_finishes_before_timeout() {
72 let result = run_with_timeout(Duration::from_secs(1), || 42);
73 assert!(result.is_ok());
74 assert_eq!(result.ok(), Some(42));
75 }
76
77 #[test]
78 fn returns_timed_out_when_operation_exceeds_timeout() {
79 let result = run_with_timeout(Duration::from_millis(50), || {
80 thread::sleep(Duration::from_secs(5));
81 42
82 });
83 assert!(result.is_err());
84 }
85
86 #[test]
87 fn wait_with_timeout_returns_success_for_fast_process() {
88 let mut child = std::process::Command::new("true").spawn().unwrap();
89 assert_eq!(wait_with_timeout(&mut child, Duration::from_secs(5)), WaitOutcome::Success);
90 }
91
92 #[test]
93 fn wait_with_timeout_kills_and_returns_timed_out_for_slow_process() {
94 let mut child = std::process::Command::new("sleep").arg("5").spawn().unwrap();
95 let start = Instant::now();
96 assert_eq!(wait_with_timeout(&mut child, Duration::from_millis(200)), WaitOutcome::TimedOut);
97 assert!(start.elapsed() < Duration::from_secs(2));
98 }
99}