use std::{
io::{self, Read, Write},
net::{TcpListener, TcpStream},
os::windows::io::AsRawSocket,
ptr,
time::Duration,
};
use windows_sys::Win32::Networking::WinSock::{
FD_SET, SOCKET, SOCKET_ERROR, TIMEVAL, WSAGetLastError, select,
};
use super::{Directions, RawSource, Ready, list::List};
pub(crate) struct Poller {
wake_read: TcpStream,
wake_write: TcpStream,
list: List,
}
impl Poller {
pub(crate) const LIVE: bool = false;
pub(crate) fn new() -> io::Result<Self> {
let listener = TcpListener::bind("127.0.0.1:0")?;
let wake_write = TcpStream::connect(listener.local_addr()?)?;
let wake_read = loop {
let (accepted, _) = listener.accept()?;
if accepted.peer_addr()? == wake_write.local_addr()? {
break accepted;
}
};
wake_read.set_nonblocking(true)?;
wake_write.set_nonblocking(true)?;
wake_read.set_nodelay(true)?;
wake_write.set_nodelay(true)?;
Ok(Self {
wake_read,
wake_write,
list: List::new(),
})
}
pub(crate) fn notify(&self) -> io::Result<()> {
match (&self.wake_write).write(&[1]) {
Ok(_) => Ok(()),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => Ok(()),
Err(e) => Err(e),
}
}
pub(crate) fn add(&self, key: usize, _descriptor: RawSource) -> io::Result<()> {
if self.list.add() > MAX_SOURCES {
self.list.delete(key);
return Err(io::Error::other(format!(
"this runtime watches at most {MAX_SOURCES} sockets"
)));
}
Ok(())
}
pub(crate) fn modify(
&self,
key: usize,
descriptor: RawSource,
_from: Directions,
to: Directions,
) -> io::Result<()> {
self.list.modify(key, descriptor, to);
Ok(())
}
pub(crate) fn delete(
&self,
key: usize,
_descriptor: RawSource,
_from: Directions,
) -> io::Result<()> {
self.list.delete(key);
Ok(())
}
pub(crate) fn wait(&self, timeout: Option<Duration>) -> io::Result<Vec<Ready>> {
let watched = self.list.snapshot();
debug_assert!(watched.len() <= MAX_SOURCES);
let mut readable = FdSet::new();
let mut writable = FdSet::new();
let mut excepted = FdSet::new();
let wake = self.wake_read.as_raw_socket() as SOCKET;
push(&mut readable, wake);
for &(_key, descriptor, directions) in &watched {
let socket = descriptor as SOCKET;
if directions.readable {
push(&mut readable, socket);
}
if directions.writable {
push(&mut writable, socket);
}
push(&mut excepted, socket);
}
let timeout = timeout.map(timeval);
let ready = unsafe {
select(
0,
ptr::from_mut(&mut readable).cast(),
offered(&mut writable),
offered(&mut excepted),
timeout.as_ref().map_or(ptr::null(), ptr::from_ref),
)
};
if ready == SOCKET_ERROR {
return Err(io::Error::from_raw_os_error(unsafe { WSAGetLastError() }));
}
if holds(&readable, wake) {
let mut buf = [0u8; 64];
while (&self.wake_read)
.read(&mut buf)
.is_ok_and(|n| n == buf.len())
{}
}
Ok(watched
.iter()
.filter_map(|&(key, descriptor, _)| {
let socket = descriptor as SOCKET;
let is_excepted = holds(&excepted, socket);
let directions = Directions {
readable: holds(&readable, socket) || is_excepted,
writable: holds(&writable, socket) || is_excepted,
};
(!directions.is_empty()).then_some(Ready {
key,
directions,
dropped: Directions::NONE,
})
})
.collect())
}
}
fn push(set: &mut FdSet, socket: SOCKET) {
set.fd_array[set.fd_count as usize] = socket;
set.fd_count += 1;
}
fn offered(set: &mut FdSet) -> *mut FD_SET {
if set.fd_count == 0 {
return ptr::null_mut();
}
ptr::from_mut(set).cast()
}
fn holds(set: &FdSet, socket: SOCKET) -> bool {
set.fd_array[..set.fd_count as usize].contains(&socket)
}
fn timeval(duration: Duration) -> TIMEVAL {
let (seconds, microseconds) = match duration.subsec_nanos().div_ceil(1_000) {
1_000_000 => (duration.as_secs().saturating_add(1), 0),
microseconds => (duration.as_secs(), microseconds),
};
TIMEVAL {
tv_sec: seconds.try_into().unwrap_or(i32::MAX),
tv_usec: i32::try_from(microseconds).unwrap_or(i32::MAX),
}
}
#[repr(C)]
struct FdSet {
fd_count: u32,
fd_array: [SOCKET; SET_SIZE],
}
impl FdSet {
fn new() -> Self {
Self {
fd_count: 0,
fd_array: [0; SET_SIZE],
}
}
}
const MAX_SOURCES: usize = SET_SIZE - 1;
const SET_SIZE: usize = 1024;