//! `TcpListener::accept()` with a timeout (`TcpListener::accept_timeout()`), and `TcpListener::connection_pending()`. //! //! See [`TcpListenerAcceptTimeout`]. #[cfg(not(target_os = "windows"))] extern crate libc; #[cfg(not(target_os = "windows"))] use libc::{time_t, c_long, POLLIN, timespec, pollfd, ppoll}; #[cfg(not(target_os = "windows"))] use std::os::fd::AsRawFd; #[cfg(not(target_os = "windows"))] use std::ptr; #[cfg(target_os = "windows")] extern crate windows_sys; #[cfg(target_os = "windows")] use windows_sys::Win32::Networking::WinSock::{POLLIN, WSAPOLLFD as pollfd, WSAPoll as poll}; #[cfg(target_os = "windows")] use std::os::windows::io::AsRawSocket; use std::net::{TcpListener, TcpStream, SocketAddr}; use std::io::Result as IoResult; use std::time::Duration; /// `TcpListener::accept_timeout()` and `TcpListener::connection_pending()`. pub trait TcpListenerAcceptTimeout { /// Returns either `None` after a time-out, or `accept()`. /// /// This is not actually a primitive provided for sockets, /// so if at least one thread is using `accept_timeout()` simultaneously with any other using `accept()` or `accept_timeout()` then: /// * if non-blocking, `Some(Err(ErrorKind::WouldBlock))` may still be returned /// * if blocking, this may still sleep /// /// since this thread may wake up because there's a connection pending, then another thread may accept it, and this thread is left in `accept()`. fn accept_timeout(&self, timeout: Option) -> Option>; /// True if `accept()` on this TcpListener will return successfully without blocking. /// /// A time-out of `None` waits forever until the condition is met (or an error occurs);
/// a time-out of `Some(Duration::ZERO)` never sleeps. /// /// Equivalent to `ppoll([{&self, POLLIN}], timeout.unwrap_or(NULL)) == {1, POLLIN}`. fn connection_pending(&self, timeout: Option) -> bool; } impl TcpListenerAcceptTimeout for TcpListener { fn accept_timeout(&self, timeout: Option) -> Option> { if !self.connection_pending(timeout) { return None; } Some(self.accept()) } #[cfg(not(target_os = "windows"))] fn connection_pending(&self, timeout: Option) -> bool { let mut fd = pollfd { fd: self.as_raw_fd(), events: POLLIN, revents: 0, }; unsafe { ppoll(&mut fd, 1, timeout.map(|timeout| { timespec { tv_sec: timeout.as_secs() as time_t, tv_nsec: timeout.subsec_nanos() as c_long, } }) .as_ref() .map(|timeout| timeout as *const _) .unwrap_or(ptr::null()), ptr::null()) == 1 && (fd.revents & POLLIN) != 0 } } #[cfg(target_os = "windows")] fn connection_pending(&self, timeout: Option) -> bool { let mut fd = pollfd { fd: self.as_raw_socket() as usize, events: POLLIN, revents: 0, }; unsafe { match timeout { None => poll(&mut fd, 1, -1) == 1 && (fd.revents & POLLIN) != 0, Some(timeout) => { let mut ms = timeout.as_millis(); while ms > i32::MAX as u128 { let ret = poll(&mut fd, 1, i32::MAX); if ret != 0 { return ret == 1 && (fd.revents & POLLIN) != 0; } ms -= i32::MAX as u128; } poll(&mut fd, 1, ms as i32) == 1 && (fd.revents & POLLIN) != 0 } } } } }