use std::io;
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Interest {
pub readable: bool,
pub writable: bool,
}
impl Interest {
pub const READABLE: Interest = Interest {
readable: true,
writable: false,
};
#[cfg(test)]
pub const WRITABLE: Interest = Interest {
readable: false,
writable: true,
};
pub fn add_writable(self) -> Interest {
Interest {
writable: true,
..self
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Event {
pub token: usize,
pub readable: bool,
pub writable: bool,
pub error: bool,
pub hangup: bool,
}
impl Event {
pub fn is_readable(&self) -> bool {
self.readable
}
pub fn is_writable(&self) -> bool {
self.writable
}
pub fn is_error(&self) -> bool {
self.error
}
pub fn is_hangup(&self) -> bool {
self.hangup
}
}
pub const WAKER_TOKEN: usize = usize::MAX;
#[cfg(target_os = "linux")]
mod linux {
use super::*;
use std::os::unix::io::RawFd;
pub type RawHandle = RawFd;
pub struct Poller {
epfd: RawFd,
}
impl Poller {
pub fn new() -> io::Result<Self> {
let epfd = unsafe { libc::epoll_create1(0) };
if epfd < 0 {
return Err(io::Error::last_os_error());
}
Ok(Poller { epfd })
}
pub fn register(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
let mut event = libc::epoll_event {
events: interest_to_epoll(interest) | libc::EPOLLET as u32,
u64: token as u64,
};
let ret = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_ADD, fd, &mut event) };
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn modify(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
let mut event = libc::epoll_event {
events: interest_to_epoll(interest) | libc::EPOLLET as u32,
u64: token as u64,
};
let ret = unsafe { libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_MOD, fd, &mut event) };
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn deregister(&mut self, fd: RawHandle) -> io::Result<()> {
let ret = unsafe {
libc::epoll_ctl(self.epfd, libc::EPOLL_CTL_DEL, fd, std::ptr::null_mut())
};
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<Vec<Event>> {
let timeout_ms = timeout.map(|d| d.as_millis() as i32).unwrap_or(-1);
let mut events: [libc::epoll_event; 256] = unsafe { std::mem::zeroed() };
loop {
let ret = unsafe {
libc::epoll_wait(
self.epfd,
events.as_mut_ptr(),
events.len() as i32,
timeout_ms,
)
};
if ret < 0 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue; }
return Err(err);
}
let mut result = Vec::with_capacity(ret as usize);
for ev in events.iter().take(ret as usize) {
let bits = ev.events;
let token = ev.u64 as usize;
result.push(Event {
token,
readable: bits & libc::EPOLLIN as u32 != 0,
writable: bits & libc::EPOLLOUT as u32 != 0,
error: bits & libc::EPOLLERR as u32 != 0,
hangup: bits & libc::EPOLLHUP as u32 != 0,
});
}
return Ok(result);
}
}
}
impl Drop for Poller {
fn drop(&mut self) {
unsafe { libc::close(self.epfd) };
}
}
fn interest_to_epoll(interest: Interest) -> u32 {
let mut events = 0;
if interest.readable {
events |= libc::EPOLLIN as u32;
}
if interest.writable {
events |= libc::EPOLLOUT as u32;
}
events
}
}
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
mod bsd {
use super::*;
use std::os::unix::io::RawFd;
pub type RawHandle = RawFd;
const EVFILT_READ: i16 = -1;
const EVFILT_WRITE: i16 = -2;
const EV_ADD: u16 = 0x0001;
const EV_DELETE: u16 = 0x0002;
const EV_ENABLE: u16 = 0x0004;
const EV_CLEAR: u16 = 0x0020; const EV_EOF: u16 = 0x8000;
const EV_ERROR: u16 = 0x4000;
const EV_RECEIPT: u16 = 0x0040;
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct Timespec {
tv_sec: isize,
tv_nsec: isize,
}
#[repr(C)]
#[derive(Clone, Copy)]
struct Kevent {
ident: usize,
filter: i16,
flags: u16,
fflags: u32,
data: isize,
udata: *mut std::ffi::c_void,
}
extern "C" {
fn kqueue() -> i32;
fn kevent(
kq: i32,
changelist: *const Kevent,
nchanges: i32,
eventlist: *mut Kevent,
nevents: i32,
timeout: *const Timespec,
) -> i32;
fn close(fd: i32) -> i32;
}
pub struct Poller {
kq: RawFd,
}
impl Poller {
pub fn new() -> io::Result<Self> {
let kq = unsafe { kqueue() };
if kq < 0 {
return Err(io::Error::last_os_error());
}
Ok(Poller { kq })
}
pub fn register(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
let mut changes = Vec::with_capacity(2);
if interest.readable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_ADD | EV_ENABLE | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut _,
});
}
if interest.writable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_ADD | EV_ENABLE | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut _,
});
}
if changes.is_empty() {
return Ok(());
}
let ret = unsafe {
kevent(
self.kq,
changes.as_ptr(),
changes.len() as i32,
std::ptr::null_mut(),
0,
std::ptr::null(),
)
};
if ret < 0 {
return Err(io::Error::last_os_error());
}
Ok(())
}
pub fn modify(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
let mut changes = Vec::with_capacity(2);
if interest.readable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_ADD | EV_ENABLE | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut _,
});
} else {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
});
}
if interest.writable {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_ADD | EV_ENABLE | EV_CLEAR,
fflags: 0,
data: 0,
udata: token as *mut _,
});
} else {
changes.push(Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
});
}
for change in changes.iter_mut() {
change.flags |= EV_RECEIPT;
}
let mut results = changes.clone();
let ret = loop {
let ret = unsafe {
kevent(
self.kq,
changes.as_ptr(),
changes.len() as i32,
results.as_mut_ptr(),
results.len() as i32,
std::ptr::null(),
)
};
if ret < 0 {
let err = std::io::Error::last_os_error();
if err.kind() == std::io::ErrorKind::Interrupted {
continue;
}
return Err(err);
}
break ret;
};
debug_assert_eq!(
ret as usize,
changes.len(),
"kevent(EV_RECEIPT) returned {ret} results for {} changes",
changes.len()
);
for res in &results[..ret as usize] {
if res.flags & EV_ERROR == 0 || res.data == 0 {
continue;
}
let is_delete = (res.filter == EVFILT_READ && !interest.readable)
|| (res.filter == EVFILT_WRITE && !interest.writable);
if is_delete && res.data as i32 == libc::ENOENT {
continue;
}
return Err(std::io::Error::from_raw_os_error(res.data as i32));
}
Ok(())
}
pub fn deregister(&mut self, fd: RawHandle) -> io::Result<()> {
let changes = [
Kevent {
ident: fd as usize,
filter: EVFILT_READ,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
Kevent {
ident: fd as usize,
filter: EVFILT_WRITE,
flags: EV_DELETE,
fflags: 0,
data: 0,
udata: std::ptr::null_mut(),
},
];
unsafe {
kevent(
self.kq,
changes.as_ptr(),
changes.len() as i32,
std::ptr::null_mut(),
0,
std::ptr::null(),
);
}
Ok(())
}
pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<Vec<Event>> {
let timespec = timeout.map(|d| Timespec {
tv_sec: d.as_secs() as isize,
tv_nsec: d.subsec_nanos() as isize,
});
let timeout_ptr = timespec
.as_ref()
.map(|t| t as *const _)
.unwrap_or(std::ptr::null());
let mut events: [Kevent; 256] = unsafe { std::mem::zeroed() };
loop {
let ret = unsafe {
kevent(
self.kq,
std::ptr::null(),
0,
events.as_mut_ptr(),
events.len() as i32,
timeout_ptr,
)
};
if ret < 0 {
let err = io::Error::last_os_error();
if err.kind() == io::ErrorKind::Interrupted {
continue; }
return Err(err);
}
use std::collections::HashMap;
let mut event_map: HashMap<usize, Event> = HashMap::new();
for i in 0..ret as usize {
let ev = &events[i];
let token = ev.udata as usize;
let entry = event_map.entry(token).or_insert(Event {
token,
readable: false,
writable: false,
error: (ev.flags & EV_ERROR) != 0,
hangup: (ev.flags & EV_EOF) != 0,
});
match ev.filter {
EVFILT_READ => entry.readable = true,
EVFILT_WRITE => entry.writable = true,
_ => {}
}
if (ev.flags & EV_ERROR) != 0 {
entry.error = true;
}
if (ev.flags & EV_EOF) != 0 {
entry.hangup = true;
}
}
return Ok(event_map.into_values().collect());
}
}
}
impl Drop for Poller {
fn drop(&mut self) {
unsafe { close(self.kq) };
}
}
}
#[cfg(target_os = "windows")]
mod windows {
use super::*;
use std::os::windows::io::RawSocket;
pub type RawHandle = RawSocket;
const POLLIN: i16 = 0x0100;
const POLLOUT: i16 = 0x0010;
const POLLERR: i16 = 0x0001;
const POLLHUP: i16 = 0x0002;
#[repr(C)]
#[derive(Clone, Copy)]
struct WSAPollFd {
fd: RawSocket,
events: i16,
revents: i16,
}
#[repr(C)]
struct WSAData {
version: u16,
high_version: u16,
max_sockets: u16,
max_udp_dg: u16,
vendor_info: *mut i8,
description: [i8; 257],
system_status: [i8; 129],
}
#[link(name = "ws2_32")]
extern "system" {
fn WSAPoll(fds: *mut WSAPollFd, nfds: u32, timeout: i32) -> i32;
fn WSAStartup(version: u16, data: *mut WSAData) -> i32;
fn WSACleanup() -> i32;
fn WSAGetLastError() -> i32;
}
struct FdEntry {
fd: RawSocket,
token: usize,
interest: Interest,
}
pub struct Poller {
entries: Vec<FdEntry>,
initialized: bool,
}
impl Poller {
pub fn new() -> io::Result<Self> {
let mut wsa_data: WSAData = unsafe { std::mem::zeroed() };
let ret = unsafe { WSAStartup(0x0202, &mut wsa_data) };
if ret != 0 {
return Err(io::Error::from_raw_os_error(ret));
}
Ok(Poller {
entries: Vec::with_capacity(64),
initialized: true,
})
}
pub fn register(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
if self.entries.iter().any(|e| e.fd == fd) {
return Err(io::Error::new(
io::ErrorKind::AlreadyExists,
"fd already registered",
));
}
self.entries.push(FdEntry {
fd,
token,
interest,
});
Ok(())
}
pub fn modify(
&mut self,
fd: RawHandle,
token: usize,
interest: Interest,
) -> io::Result<()> {
if let Some(entry) = self.entries.iter_mut().find(|e| e.fd == fd) {
entry.token = token;
entry.interest = interest;
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "fd not registered"))
}
}
pub fn deregister(&mut self, fd: RawHandle) -> io::Result<()> {
if let Some(pos) = self.entries.iter().position(|e| e.fd == fd) {
self.entries.swap_remove(pos);
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "fd not registered"))
}
}
pub fn poll(&mut self, timeout: Option<Duration>) -> io::Result<Vec<Event>> {
if self.entries.is_empty() {
if let Some(dur) = timeout {
std::thread::sleep(dur);
}
return Ok(Vec::new());
}
let timeout_ms = timeout.map(|d| d.as_millis() as i32).unwrap_or(-1);
let mut pollfds: Vec<WSAPollFd> = self
.entries
.iter()
.map(|e| WSAPollFd {
fd: e.fd,
events: interest_to_poll(&e.interest),
revents: 0,
})
.collect();
loop {
let ret =
unsafe { WSAPoll(pollfds.as_mut_ptr(), pollfds.len() as u32, timeout_ms) };
if ret < 0 {
let err = unsafe { WSAGetLastError() };
if err == 10004 {
continue; }
return Err(io::Error::from_raw_os_error(err));
}
let mut result = Vec::new();
for (i, pollfd) in pollfds.iter().enumerate() {
if pollfd.revents != 0 {
result.push(Event {
token: self.entries[i].token,
readable: (pollfd.revents & POLLIN) != 0,
writable: (pollfd.revents & POLLOUT) != 0,
error: (pollfd.revents & POLLERR) != 0,
hangup: (pollfd.revents & POLLHUP) != 0,
});
}
}
return Ok(result);
}
}
}
impl Drop for Poller {
fn drop(&mut self) {
if self.initialized {
unsafe { WSACleanup() };
}
}
}
fn interest_to_poll(interest: &Interest) -> i16 {
let mut events: i16 = 0;
if interest.readable {
events |= POLLIN;
}
if interest.writable {
events |= POLLOUT;
}
events
}
}
#[cfg(target_os = "linux")]
pub use linux::{Poller, RawHandle};
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
pub use bsd::{Poller, RawHandle};
#[cfg(target_os = "windows")]
pub use windows::{Poller, RawHandle};
#[cfg(target_os = "linux")]
mod waker_backend {
use super::*;
use std::os::unix::io::RawFd;
use std::sync::Arc;
struct WakerFd(RawFd);
impl Drop for WakerFd {
fn drop(&mut self) {
unsafe {
libc::close(self.0);
}
}
}
pub struct Waker {
fd: Arc<WakerFd>,
}
#[derive(Clone)]
pub struct WakeHandle {
fd: Arc<WakerFd>,
}
pub fn waker_pair() -> io::Result<(Waker, WakeHandle)> {
let fd = unsafe { libc::eventfd(0, libc::EFD_NONBLOCK | libc::EFD_CLOEXEC) };
if fd < 0 {
return Err(io::Error::last_os_error());
}
let shared = Arc::new(WakerFd(fd));
Ok((Waker { fd: shared.clone() }, WakeHandle { fd: shared }))
}
impl Waker {
pub fn raw_handle(&self) -> RawFd {
self.fd.0
}
pub fn drain(&self) {
let mut buf = [0u8; 8];
loop {
let n = unsafe { libc::read(self.fd.0, buf.as_mut_ptr() as *mut libc::c_void, 8) };
if n != 8 {
break;
}
}
}
}
impl WakeHandle {
pub fn wake(&self) {
let val: u64 = 1;
let _ = unsafe { libc::write(self.fd.0, &val as *const u64 as *const libc::c_void, 8) };
}
}
}
#[cfg(any(
target_os = "macos",
target_os = "freebsd",
target_os = "openbsd",
target_os = "netbsd"
))]
mod waker_backend {
use super::*;
use std::os::unix::io::RawFd;
use std::sync::Arc;
struct Pipe {
read_fd: RawFd,
write_fd: RawFd,
}
impl Drop for Pipe {
fn drop(&mut self) {
unsafe {
libc::close(self.read_fd);
libc::close(self.write_fd);
}
}
}
pub struct Waker {
pipe: Arc<Pipe>,
}
#[derive(Clone)]
pub struct WakeHandle {
pipe: Arc<Pipe>,
}
pub fn waker_pair() -> io::Result<(Waker, WakeHandle)> {
let mut fds = [0 as libc::c_int; 2];
let ret = unsafe { libc::pipe(fds.as_mut_ptr()) };
if ret < 0 {
return Err(io::Error::last_os_error());
}
let read_fd = fds[0] as RawFd;
let write_fd = fds[1] as RawFd;
if let Err(e) =
set_nonblocking_cloexec(read_fd).and_then(|_| set_nonblocking_cloexec(write_fd))
{
unsafe {
libc::close(read_fd);
libc::close(write_fd);
}
return Err(e);
}
let shared = Arc::new(Pipe { read_fd, write_fd });
Ok((
Waker {
pipe: shared.clone(),
},
WakeHandle { pipe: shared },
))
}
fn set_nonblocking_cloexec(fd: RawFd) -> io::Result<()> {
unsafe {
let flags = libc::fcntl(fd, libc::F_GETFL);
if flags < 0 {
return Err(io::Error::last_os_error());
}
if libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) < 0 {
return Err(io::Error::last_os_error());
}
if libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) < 0 {
return Err(io::Error::last_os_error());
}
}
Ok(())
}
impl Waker {
pub fn raw_handle(&self) -> RawFd {
self.pipe.read_fd
}
pub fn drain(&self) {
let mut buf = [0u8; 64];
loop {
let n = unsafe {
libc::read(
self.pipe.read_fd,
buf.as_mut_ptr() as *mut libc::c_void,
buf.len(),
)
};
if n <= 0 || (n as usize) < buf.len() {
break;
}
}
}
}
impl WakeHandle {
pub fn wake(&self) {
let byte: u8 = 1;
let _ = unsafe {
libc::write(
self.pipe.write_fd,
&byte as *const u8 as *const libc::c_void,
1,
)
};
}
}
}
#[cfg(target_os = "windows")]
mod waker_backend {
use super::*;
use std::net::{TcpListener, TcpStream};
use std::os::windows::io::{AsRawSocket, RawSocket};
use std::sync::Arc;
#[link(name = "ws2_32")]
extern "system" {
fn send(s: RawSocket, buf: *const i8, len: i32, flags: i32) -> i32;
fn recv(s: RawSocket, buf: *mut i8, len: i32, flags: i32) -> i32;
}
struct Pair {
reader: TcpStream,
writer: TcpStream,
}
pub struct Waker {
pair: Arc<Pair>,
}
#[derive(Clone)]
pub struct WakeHandle {
pair: Arc<Pair>,
}
pub fn waker_pair() -> io::Result<(Waker, WakeHandle)> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let addr = listener.local_addr()?;
let writer = TcpStream::connect(addr)?;
let (reader, _) = listener.accept()?;
reader.set_nonblocking(true)?;
writer.set_nonblocking(true)?;
let shared = Arc::new(Pair { reader, writer });
Ok((
Waker {
pair: shared.clone(),
},
WakeHandle { pair: shared },
))
}
impl Waker {
pub fn raw_handle(&self) -> RawSocket {
self.pair.reader.as_raw_socket()
}
pub fn drain(&self) {
let mut buf = [0i8; 64];
loop {
let n = unsafe {
recv(
self.pair.reader.as_raw_socket(),
buf.as_mut_ptr(),
buf.len() as i32,
0,
)
};
if n <= 0 || (n as usize) < buf.len() {
break;
}
}
}
}
impl WakeHandle {
pub fn wake(&self) {
let byte: i8 = 1;
let _ = unsafe { send(self.pair.writer.as_raw_socket(), &byte as *const i8, 1, 0) };
}
}
}
pub use waker_backend::{waker_pair, WakeHandle, Waker};
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use std::net::{TcpListener, TcpStream};
#[test]
fn test_poller_basic() {
let mut poller = Poller::new().expect("Failed to create poller");
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind");
let addr = listener.local_addr().expect("Failed to get address");
let client = TcpStream::connect(addr).expect("Failed to connect");
client
.set_nonblocking(true)
.expect("Failed to set nonblocking");
let (mut server, _) = listener.accept().expect("Failed to accept");
server
.set_nonblocking(true)
.expect("Failed to set nonblocking");
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let client_fd = client.as_raw_fd();
let server_fd = server.as_raw_fd();
poller
.register(client_fd, 1, Interest::READABLE)
.expect("Failed to register");
poller
.register(server_fd, 2, Interest::WRITABLE)
.expect("Failed to register");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 2 && e.is_writable()));
server.write_all(b"hello").expect("Failed to write");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 1 && e.is_readable()));
poller.deregister(client_fd).expect("Failed to deregister");
poller.deregister(server_fd).expect("Failed to deregister");
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawSocket;
let client_fd = client.as_raw_socket();
let server_fd = server.as_raw_socket();
poller
.register(client_fd, 1, Interest::READABLE)
.expect("Failed to register");
poller
.register(server_fd, 2, Interest::WRITABLE)
.expect("Failed to register");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 2 && e.is_writable()));
server.write_all(b"hello").expect("Failed to write");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 1 && e.is_readable()));
poller.deregister(client_fd).expect("Failed to deregister");
poller.deregister(server_fd).expect("Failed to deregister");
}
}
#[cfg(unix)]
#[test]
fn poll_reports_correct_tokens_for_multiple_ready_fds() {
use std::os::unix::io::AsRawFd;
let mut poller = Poller::new().expect("Failed to create poller");
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind");
let addr = listener.local_addr().expect("Failed to get address");
let _clients: Vec<TcpStream> = (0..3)
.map(|_| TcpStream::connect(addr).expect("Failed to connect"))
.collect();
let servers: Vec<TcpStream> = (0..3)
.map(|_| listener.accept().expect("Failed to accept").0)
.collect();
for (i, server) in servers.iter().enumerate() {
server
.set_nonblocking(true)
.expect("Failed to set nonblocking");
poller
.register(server.as_raw_fd(), (i + 1) * 10, Interest::WRITABLE)
.expect("Failed to register");
}
let events = poller
.poll(Some(Duration::from_millis(200)))
.expect("Failed to poll");
let mut tokens: Vec<usize> = events
.iter()
.filter(|e| e.is_writable())
.map(|e| e.token)
.collect();
tokens.sort_unstable();
assert_eq!(
tokens,
vec![10, 20, 30],
"every simultaneously-ready fd must report its own token"
);
}
#[test]
fn test_deregister_no_events() {
let mut poller = Poller::new().expect("Failed to create poller");
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind");
let addr = listener.local_addr().expect("Failed to get address");
let client = TcpStream::connect(addr).expect("Failed to connect");
client
.set_nonblocking(true)
.expect("Failed to set nonblocking");
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let fd = client.as_raw_fd();
poller
.register(fd, 1, Interest::READABLE)
.expect("Failed to register");
poller.deregister(fd).expect("Failed to deregister");
let events = poller
.poll(Some(Duration::from_millis(50)))
.expect("Failed to poll");
assert!(!events.iter().any(|e| e.token == 1));
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawSocket;
let fd = client.as_raw_socket();
poller
.register(fd, 1, Interest::READABLE)
.expect("Failed to register");
poller.deregister(fd).expect("Failed to deregister");
let events = poller
.poll(Some(Duration::from_millis(50)))
.expect("Failed to poll");
assert!(!events.iter().any(|e| e.token == 1));
}
}
#[cfg(target_os = "linux")]
#[test]
fn waker_wakes_the_poller_and_drains() {
let mut poller = Poller::new().expect("Failed to create poller");
let (waker, handle) = waker_pair().expect("Failed to create waker");
poller
.register(waker.raw_handle(), WAKER_TOKEN, Interest::READABLE)
.expect("Failed to register waker");
let events = poller.poll(Some(Duration::from_millis(50))).expect("poll");
assert!(
events.iter().all(|e| e.token != WAKER_TOKEN),
"no wake => no waker event"
);
handle.wake();
handle.wake();
let events = poller.poll(Some(Duration::from_millis(500))).expect("poll");
assert!(
events
.iter()
.any(|e| e.token == WAKER_TOKEN && e.is_readable()),
"wake() must produce a readable event on WAKER_TOKEN"
);
waker.drain();
let events = poller.poll(Some(Duration::from_millis(50))).expect("poll");
assert!(
events.iter().all(|e| e.token != WAKER_TOKEN),
"drain() must clear the wake token"
);
poller.deregister(waker.raw_handle()).ok();
}
#[test]
fn test_modify_interest() {
let mut poller = Poller::new().expect("Failed to create poller");
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind");
let addr = listener.local_addr().expect("Failed to get address");
let client = TcpStream::connect(addr).expect("Failed to connect");
client
.set_nonblocking(true)
.expect("Failed to set nonblocking");
let (server, _) = listener.accept().expect("Failed to accept");
server
.set_nonblocking(true)
.expect("Failed to set nonblocking");
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
let server_fd = server.as_raw_fd();
poller
.register(server_fd, 1, Interest::READABLE)
.expect("Failed to register");
poller
.modify(server_fd, 1, Interest::WRITABLE)
.expect("Failed to modify");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 1 && e.is_writable()));
poller.deregister(server_fd).expect("Failed to deregister");
}
#[cfg(windows)]
{
use std::os::windows::io::AsRawSocket;
let server_fd = server.as_raw_socket();
poller
.register(server_fd, 1, Interest::READABLE)
.expect("Failed to register");
poller
.modify(server_fd, 1, Interest::WRITABLE)
.expect("Failed to modify");
let events = poller
.poll(Some(Duration::from_millis(100)))
.expect("Failed to poll");
assert!(events.iter().any(|e| e.token == 1 && e.is_writable()));
poller.deregister(server_fd).expect("Failed to deregister");
}
}
}