tcplistener_accept_timeout/
lib.rs

1//! `TcpListener::accept()` with a timeout (`TcpListener::accept_timeout()`), and `TcpListener::connection_pending()`.
2//!
3//! See [`TcpListenerAcceptTimeout`].
4
5
6#[cfg(not(target_os = "windows"))]
7extern crate libc;
8#[cfg(not(target_os = "windows"))]
9use libc::{time_t, c_long, POLLIN, timespec, pollfd, ppoll};
10#[cfg(not(target_os = "windows"))]
11use std::os::fd::AsRawFd;
12#[cfg(not(target_os = "windows"))]
13use std::ptr;
14
15#[cfg(target_os = "windows")]
16extern crate windows_sys;
17#[cfg(target_os = "windows")]
18use windows_sys::Win32::Networking::WinSock::{POLLIN, WSAPOLLFD as pollfd, WSAPoll as poll};
19#[cfg(target_os = "windows")]
20use std::os::windows::io::AsRawSocket;
21
22use std::net::{TcpListener, TcpStream, SocketAddr};
23use std::io::Result as IoResult;
24use std::time::Duration;
25
26
27/// `TcpListener::accept_timeout()` and `TcpListener::connection_pending()`.
28pub trait TcpListenerAcceptTimeout {
29    /// Returns either `None` after a time-out, or `accept()`.
30    ///
31    /// This is not actually a primitive provided for sockets,
32    /// so if at least one thread is using `accept_timeout()` simultaneously with any other using `accept()` or `accept_timeout()` then:
33    ///   * if non-blocking, `Some(Err(ErrorKind::WouldBlock))` may still be returned
34    ///   * if blocking, this may still sleep
35    ///
36    /// 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()`.
37    fn accept_timeout(&self, timeout: Option<Duration>) -> Option<IoResult<(TcpStream, SocketAddr)>>;
38
39    /// True if `accept()` on this TcpListener will return successfully without blocking.
40    ///
41    /// A time-out of `None` waits forever until the condition is met (or an error occurs);<br />
42    /// a time-out of `Some(Duration::ZERO)` never sleeps.
43    ///
44    /// Equivalent to `ppoll([{&self, POLLIN}], timeout.unwrap_or(NULL)) == {1, POLLIN}`.
45    fn connection_pending(&self, timeout: Option<Duration>) -> bool;
46}
47
48impl TcpListenerAcceptTimeout for TcpListener {
49    fn accept_timeout(&self, timeout: Option<Duration>) -> Option<IoResult<(TcpStream, SocketAddr)>> {
50        if !self.connection_pending(timeout) {
51            return None;
52        }
53        Some(self.accept())
54    }
55
56    #[cfg(not(target_os = "windows"))]
57    fn connection_pending(&self, timeout: Option<Duration>) -> bool {
58        let mut fd = pollfd {
59            fd: self.as_raw_fd(),
60            events: POLLIN,
61            revents: 0,
62        };
63        unsafe {
64            ppoll(&mut fd,
65                  1,
66                  timeout.map(|timeout| {
67                          timespec {
68                              tv_sec: timeout.as_secs() as time_t,
69                              tv_nsec: timeout.subsec_nanos() as c_long,
70                          }
71                      })
72                      .as_ref()
73                      .map(|timeout| timeout as *const _)
74                      .unwrap_or(ptr::null()),
75                  ptr::null()) == 1 && (fd.revents & POLLIN) != 0
76        }
77    }
78
79    #[cfg(target_os = "windows")]
80    fn connection_pending(&self, timeout: Option<Duration>) -> bool {
81        let mut fd = pollfd {
82            fd: self.as_raw_socket() as usize,
83            events: POLLIN,
84            revents: 0,
85        };
86        unsafe {
87            match timeout {
88                None => poll(&mut fd, 1, -1) == 1 && (fd.revents & POLLIN) != 0,
89                Some(timeout) => {
90                    let mut ms = timeout.as_millis();
91                    while ms > i32::MAX as u128 {
92                        let ret = poll(&mut fd, 1, i32::MAX);
93                        if ret != 0 {
94                            return ret == 1 && (fd.revents & POLLIN) != 0;
95                        }
96                        ms -= i32::MAX as u128;
97                    }
98                    poll(&mut fd, 1, ms as i32) == 1 && (fd.revents & POLLIN) != 0
99                }
100            }
101        }
102    }
103}