use std::io;
use std::sync::Once;
use super::ReactorBackend;
use crate::platform::sys::{Event, Events, Interest, RawSource};
const POLLIN: i16 = 0x0100;
const POLLOUT: i16 = 0x0010;
const POLLHUP: i16 = 0x0002;
const POLLERR: i16 = 0x0001;
pub(crate) struct IocpReactor {
fds: Vec<windows_sys::Win32::Networking::WinSock::WSAPOLLFD>,
tokens: Vec<usize>,
}
impl ReactorBackend for IocpReactor {
fn new() -> io::Result<Self> {
init_winsock()?;
Ok(Self {
fds: Vec::with_capacity(64),
tokens: Vec::with_capacity(64),
})
}
fn register(&self, source: RawSource, token: usize, interest: Interest) -> io::Result<()> {
let this = unsafe { &mut *(self as *const Self as *mut Self) };
this.fds.push(windows_sys::Win32::Networking::WinSock::WSAPOLLFD {
fd: source as usize,
events: interest_to_wsa(interest),
revents: 0,
});
this.tokens.push(token);
Ok(())
}
fn reregister(&self, source: RawSource, token: usize, interest: Interest) -> io::Result<()> {
let this = unsafe { &mut *(self as *const Self as *mut Self) };
for (i, fd) in this.fds.iter_mut().enumerate() {
if fd.fd == source as usize && this.tokens[i] == token {
fd.events = interest_to_wsa(interest);
return Ok(());
}
}
Err(io::Error::new(io::ErrorKind::NotFound, "source not registered"))
}
fn deregister(&self, source: RawSource) -> io::Result<()> {
let this = unsafe { &mut *(self as *const Self as *mut Self) };
if let Some(pos) = this.fds.iter().position(|fd| fd.fd == source as usize) {
this.fds.swap_remove(pos);
this.tokens.swap_remove(pos);
Ok(())
} else {
Err(io::Error::new(io::ErrorKind::NotFound, "source not registered"))
}
}
fn poll(&self, events: &mut Events, timeout_ms: Option<u64>) -> io::Result<usize> {
let this = unsafe { &mut *(self as *const Self as *mut Self) };
events.clear();
if this.fds.is_empty() {
if let Some(ms) = timeout_ms {
std::thread::sleep(std::time::Duration::from_millis(ms));
}
return Ok(0);
}
let timeout = timeout_ms.map(|ms| ms as i32).unwrap_or(-1);
let n = unsafe {
windows_sys::Win32::Networking::WinSock::WSAPoll(
this.fds.as_mut_ptr(),
this.fds.len() as u32,
timeout,
)
};
if n < 0 {
return Err(io::Error::last_os_error());
}
for i in 0..this.fds.len() {
let revents = this.fds[i].revents;
if revents != 0 {
let readable = revents & (POLLIN | POLLHUP | POLLERR) != 0;
let writable = revents & (POLLOUT | POLLHUP | POLLERR) != 0;
events.push(Event::new(this.tokens[i], readable, writable));
this.fds[i].revents = 0;
}
}
Ok(events.len())
}
}
#[inline]
fn interest_to_wsa(interest: Interest) -> i16 {
let mut events = 0i16;
if interest.is_readable() {
events |= POLLIN;
}
if interest.is_writable() {
events |= POLLOUT;
}
events
}
fn init_winsock() -> io::Result<()> {
static INIT: Once = Once::new();
static mut INIT_RESULT: io::Result<()> = Ok(());
INIT.call_once(|| {
let mut wsa_data = unsafe { std::mem::zeroed() };
let ret = unsafe {
windows_sys::Win32::Networking::WinSock::WSAStartup(
0x0202, &mut wsa_data,
)
};
if ret != 0 {
unsafe {
INIT_RESULT = Err(io::Error::from_raw_os_error(ret));
}
}
});
let result = unsafe { &INIT_RESULT };
match result {
Ok(()) => Ok(()),
Err(e) => Err(io::Error::from_raw_os_error(
e.raw_os_error().unwrap_or(0),
)),
}
}