Skip to main content

scx_pandemonium/cli/
death_pipe.rs

1use std::sync::atomic::{AtomicBool, Ordering};
2
3/// Create a pipe for parent-death detection.
4/// Returns (read_fd, write_fd). Neither end has CLOEXEC set.
5/// Parent holds write_fd open. Child monitors read_fd for POLLHUP.
6pub fn create_death_pipe() -> Result<(i32, i32), std::io::Error> {
7    let mut fds = [0i32; 2];
8    let ret = unsafe { libc::pipe2(fds.as_mut_ptr(), 0) };
9    if ret < 0 {
10        return Err(std::io::Error::last_os_error());
11    }
12    Ok((fds[0], fds[1]))
13}
14
15pub fn close_fd(fd: i32) {
16    if fd >= 0 {
17        unsafe {
18            libc::close(fd);
19        }
20    }
21}
22
23/// Monitor a death pipe FD in a background thread.
24/// When the write end closes (parent dies), POLLHUP fires and `running`
25/// is set to false, triggering the probe's graceful shutdown.
26pub fn spawn_death_watcher(death_fd: i32, running: &'static AtomicBool) {
27    std::thread::Builder::new()
28        .name("death-watcher".into())
29        .spawn(move || {
30            let mut pfd = libc::pollfd {
31                fd: death_fd,
32                events: libc::POLLIN,
33                revents: 0,
34            };
35            while running.load(Ordering::Relaxed) {
36                let ret = unsafe { libc::poll(&mut pfd, 1, 100) };
37                if ret > 0 && (pfd.revents & (libc::POLLHUP | libc::POLLERR)) != 0 {
38                    running.store(false, Ordering::Relaxed);
39                    break;
40                }
41                if ret < 0 {
42                    let err = std::io::Error::last_os_error();
43                    if err.kind() != std::io::ErrorKind::Interrupted {
44                        running.store(false, Ordering::Relaxed);
45                        break;
46                    }
47                }
48            }
49            unsafe {
50                libc::close(death_fd);
51            }
52        })
53        .ok();
54}