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
7#[cfg(any(target_os = "linux", target_os = "freebsd"))]
8pub(crate) struct EventFd(std::fs::File);
9#[cfg(any(target_os = "linux", target_os = "freebsd"))]
10impl EventFd {
11    pub(crate) fn new() -> io::Result<Self> {
12        #[cfg(not(target_os = "espidf"))]
13        let flags = libc::EFD_CLOEXEC | libc::EFD_NONBLOCK;
14        // ESP-IDF is EFD_NONBLOCK by default and errors if you try to pass this flag.
15        #[cfg(target_os = "espidf")]
16        let flags = 0;
17        let event_fd = unsafe { libc::eventfd(0, flags) };
18        if event_fd < 0 {
19            return Err(io::Error::last_os_error());
20        }
21        use std::os::fd::FromRawFd;
22        let file = unsafe { std::fs::File::from_raw_fd(event_fd) };
23        Ok(Self(file))
24    }
25    fn wake(&self) -> io::Result<()> {
26        use std::io::Write;
27        let buf: [u8; 8] = 1u64.to_ne_bytes();
28        match (&self.0).write_all(&buf) {
29            Ok(_) => Ok(()),
30            Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => Ok(()),
31            Err(err) => Err(err),
32        }
33    }
34    fn as_event_fd(&self) -> libc::c_int {
35        self.0.as_raw_fd() as _
36    }
37}
38#[cfg(target_os = "macos")]
39struct EventFd(libc::c_int, libc::c_int);
40#[cfg(target_os = "macos")]
41impl EventFd {
42    fn new() -> io::Result<Self> {
43        let mut fds: [libc::c_int; 2] = [0; 2];
44        if unsafe { libc::pipe(fds.as_mut_ptr()) } == -1 {
45            return Err(io::Error::last_os_error());
46        }
47        let read_fd = fds[0];
48        let write_fd = fds[1];
49        Ok(Self(read_fd, write_fd))
50    }
51    fn wake(&self) -> io::Result<()> {
52        let buf: [u8; 8] = 1u64.to_ne_bytes();
53        let res = unsafe { libc::write(self.1, buf.as_ptr() as *const libc::c_void, buf.len()) };
54        if res == -1 {
55            Err(io::Error::last_os_error())
56        } else {
57            Ok(())
58        }
59    }
60    fn as_event_fd(&self) -> libc::c_int {
61        self.0
62    }
63}
64#[cfg(target_os = "macos")]
65impl Drop for EventFd {
66    fn drop(&mut self) {
67        unsafe {
68            let _ = libc::close(self.0);
69            let _ = libc::close(self.1);
70        }
71    }
72}
73impl RouteListener {
74    pub(crate) fn wait(&self) -> io::Result<()> {
75        let fd = self.as_raw_fd() as libc::c_int;
76
77        let event_fd = self.shutdown_handle.event_fd.as_event_fd();
78        let mut readfds: libc::fd_set = unsafe { std::mem::zeroed() };
79        unsafe {
80            libc::FD_SET(fd, &mut readfds);
81            libc::FD_SET(event_fd, &mut readfds);
82        }
83        let result = unsafe {
84            libc::select(
85                fd.max(event_fd) + 1,
86                &mut readfds,
87                std::ptr::null_mut(),
88                std::ptr::null_mut(),
89                std::ptr::null_mut(),
90            )
91        };
92        if self.shutdown_handle.is_shutdown.load(Ordering::Relaxed) {
93            return Err(io::Error::new(io::ErrorKind::Interrupted, "shutdown"));
94        }
95        if result == -1 {
96            return Err(io::Error::last_os_error());
97        }
98        if result == 0 {
99            return Err(io::Error::from(io::ErrorKind::TimedOut));
100        }
101        Ok(())
102    }
103    /// Retrieves a shutdown handle for the RouteListener.
104    pub fn shutdown_handle(&self) -> io::Result<RouteListenerShutdown> {
105        Ok(self.shutdown_handle.clone())
106    }
107}
108
109/// Shutdown handle for the RouteListener, used to stop listening.
110#[derive(Clone)]
111pub struct RouteListenerShutdown {
112    is_shutdown: Arc<AtomicBool>,
113    event_fd: Arc<EventFd>,
114}
115impl RouteListenerShutdown {
116    pub(crate) fn new() -> io::Result<Self> {
117        Ok(Self {
118            is_shutdown: Arc::new(Default::default()),
119            event_fd: Arc::new(EventFd::new()?),
120        })
121    }
122    /// Shuts down the RouteListener.
123    pub fn shutdown(&self) -> io::Result<()> {
124        self.is_shutdown.store(true, Ordering::Relaxed);
125        self.event_fd.wake()
126    }
127}