route_manager/unix/
shutdown.rs

1use crate::RouteListener;
2use std::io;
3use std::os::fd::AsRawFd;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::Arc;
6
7struct EventFd(libc::c_int, libc::c_int);
8impl EventFd {
9    fn new() -> io::Result<Self> {
10        let mut fds: [libc::c_int; 2] = [0; 2];
11        if unsafe { libc::pipe(fds.as_mut_ptr()) } == -1 {
12            return Err(io::Error::last_os_error());
13        }
14        let read_fd = fds[0];
15        let write_fd = fds[1];
16        Ok(Self(read_fd, write_fd))
17    }
18    fn wake(&self) -> io::Result<()> {
19        let buf: [u8; 8] = 1u64.to_ne_bytes();
20        let res = unsafe { libc::write(self.1, buf.as_ptr() as *const libc::c_void, buf.len()) };
21        if res == -1 {
22            Err(io::Error::last_os_error())
23        } else {
24            Ok(())
25        }
26    }
27    fn as_event_fd(&self) -> libc::c_int {
28        self.0
29    }
30}
31impl Drop for EventFd {
32    fn drop(&mut self) {
33        unsafe {
34            let _ = libc::close(self.0);
35            let _ = libc::close(self.1);
36        }
37    }
38}
39impl RouteListener {
40    pub(crate) fn wait(&self) -> io::Result<()> {
41        let fd = self.as_raw_fd() as libc::c_int;
42
43        let event_fd = self.shutdown_handle.event_fd.as_event_fd();
44        let mut readfds: libc::fd_set = unsafe { std::mem::zeroed() };
45        unsafe {
46            libc::FD_SET(fd, &mut readfds);
47            libc::FD_SET(event_fd, &mut readfds);
48        }
49        let result = unsafe {
50            libc::select(
51                fd.max(event_fd) + 1,
52                &mut readfds,
53                std::ptr::null_mut(),
54                std::ptr::null_mut(),
55                std::ptr::null_mut(),
56            )
57        };
58        if self.shutdown_handle.is_shutdown.load(Ordering::Relaxed) {
59            return Err(io::Error::new(io::ErrorKind::Interrupted, "shutdown"));
60        }
61        if result == -1 {
62            return Err(io::Error::last_os_error());
63        }
64        if result == 0 {
65            return Err(io::Error::from(io::ErrorKind::TimedOut));
66        }
67        Ok(())
68    }
69    /// Retrieves a shutdown handle for the RouteListener.
70    pub fn shutdown_handle(&self) -> io::Result<RouteListenerShutdown> {
71        Ok(self.shutdown_handle.clone())
72    }
73}
74
75/// Shutdown handle for the RouteListener, used to stop listening.
76#[derive(Clone)]
77pub struct RouteListenerShutdown {
78    is_shutdown: Arc<AtomicBool>,
79    event_fd: Arc<EventFd>,
80}
81impl RouteListenerShutdown {
82    pub(crate) fn new() -> io::Result<Self> {
83        Ok(Self {
84            is_shutdown: Arc::new(Default::default()),
85            event_fd: Arc::new(EventFd::new()?),
86        })
87    }
88    /// Shuts down the RouteListener.
89    pub fn shutdown(&self) -> io::Result<()> {
90        self.is_shutdown.store(true, Ordering::Relaxed);
91        self.event_fd.wake()
92    }
93}