supercov_engine/
progress.rs1#[cfg(unix)]
12use std::io::Write;
13use std::{
14 io::IsTerminal,
15 sync::{
16 Arc,
17 atomic::{AtomicBool, Ordering},
18 },
19 thread::JoinHandle,
20 time::Duration,
21};
22
23const QUIET_PERIOD: Duration = Duration::from_millis(120);
25
26#[cfg(unix)]
32fn status_output() -> Option<std::fs::File> {
33 use std::os::fd::FromRawFd;
34 let descriptor = unsafe { libc::dup(2) };
35 (descriptor >= 0).then(|| unsafe { std::fs::File::from_raw_fd(descriptor) })
36}
37
38pub struct ProgressLine {
39 stop: Arc<AtomicBool>,
40 handle: Option<JoinHandle<()>>,
41}
42
43impl ProgressLine {
44 pub fn start(message: &'static str) -> Option<Self> {
45 if cfg!(not(unix)) || !std::io::stderr().is_terminal() {
46 return None;
47 }
48 Self::start_on_terminal(message)
49 }
50
51 #[cfg(unix)]
52 fn start_on_terminal(message: &'static str) -> Option<Self> {
53 let mut output = status_output()?;
54 let stop = Arc::new(AtomicBool::new(false));
55 let flag = Arc::clone(&stop);
56 let handle = std::thread::spawn(move || {
57 std::thread::sleep(QUIET_PERIOD);
58 if flag.load(Ordering::Relaxed) {
59 return;
60 }
61 let _ = writeln!(output, "❋ {message}…");
62 });
63 Some(Self {
64 stop,
65 handle: Some(handle),
66 })
67 }
68
69 #[cfg(not(unix))]
70 fn start_on_terminal(_message: &'static str) -> Option<Self> {
71 None
74 }
75}
76
77impl Drop for ProgressLine {
78 fn drop(&mut self) {
79 self.stop.store(true, Ordering::Relaxed);
80 if let Some(handle) = self.handle.take() {
81 let _ = handle.join();
82 }
83 }
84}
85
86#[cfg(all(test, unix))]
87mod tests {
88 use super::*;
89
90 #[test]
96 fn status_line_never_needs_stds_stderr_lock() {
97 let diagnostics = std::io::stderr().lock();
98 let line = ProgressLine::start_on_terminal("proving the status line stays lock-free");
99 std::thread::sleep(QUIET_PERIOD * 2);
100 let (sender, receiver) = std::sync::mpsc::channel();
101 std::thread::spawn(move || {
102 drop(line);
103 let _ = sender.send(());
104 });
105 let dropped = receiver.recv_timeout(Duration::from_secs(10));
106 drop(diagnostics);
107 assert!(
108 dropped.is_ok(),
109 "dropping the status line deadlocked against std's stderr lock"
110 );
111 }
112}