use std::collections::HashMap;
use std::io;
use std::time::Duration;
use super::{Completion, Event, Interest, RawFd, Reactor, Token};
pub struct IocpReactor {
max_events: usize,
registrations: HashMap<Token, Registration>,
next_token: usize,
pending: Vec<PendingOp>,
}
struct Registration {
fd: RawFd,
interest: Interest,
}
#[allow(dead_code)]
struct PendingOp {
token: Token,
op_type: OpType,
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy)]
enum OpType {
Read,
Write,
}
impl IocpReactor {
pub fn new(max_events: usize) -> io::Result<Self> {
#[cfg(target_os = "windows")]
{
Ok(Self {
max_events,
registrations: HashMap::new(),
next_token: 1,
pending: Vec::new(),
})
}
#[cfg(not(target_os = "windows"))]
{
Ok(Self {
max_events,
registrations: HashMap::new(),
next_token: 1,
pending: Vec::new(),
})
}
}
pub fn is_available() -> bool {
cfg!(target_os = "windows")
}
}
impl Reactor for IocpReactor {
fn poll(&mut self, timeout: Option<Duration>) -> io::Result<Vec<Event>> {
#[cfg(target_os = "windows")]
{
if let Some(duration) = timeout {
std::thread::sleep(duration.min(Duration::from_millis(100)));
}
let events: Vec<Event> = self
.pending
.iter()
.filter_map(|op| {
if self.registrations.contains_key(&op.token) {
Some(Event::new(op.token).with_readable())
} else {
None
}
})
.collect();
self.pending.clear();
Ok(events)
}
#[cfg(not(target_os = "windows"))]
{
if let Some(duration) = timeout {
std::thread::sleep(duration.min(Duration::from_millis(100)));
}
Ok(Vec::new())
}
}
fn register(&mut self, fd: RawFd, interest: Interest) -> io::Result<Token> {
let token = Token(self.next_token);
self.next_token += 1;
#[cfg(target_os = "windows")]
{
}
self.registrations
.insert(token, Registration { fd, interest });
Ok(token)
}
fn modify(&mut self, token: Token, interest: Interest) -> io::Result<()> {
if let Some(reg) = self.registrations.get_mut(&token) {
reg.interest = interest;
Ok(())
} else {
Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
))
}
}
fn deregister(&mut self, token: Token) -> io::Result<()> {
self.registrations.remove(&token);
self.pending.retain(|op| op.token != token);
Ok(())
}
fn submit_read(&mut self, token: Token, _buf: &mut [u8]) -> io::Result<Option<Completion>> {
if !self.registrations.contains_key(&token) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
));
}
self.pending.push(PendingOp {
token,
op_type: OpType::Read,
});
Ok(None)
}
fn submit_write(&mut self, token: Token, _buf: &[u8]) -> io::Result<Option<Completion>> {
if !self.registrations.contains_key(&token) {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"Token not registered",
));
}
self.pending.push(PendingOp {
token,
op_type: OpType::Write,
});
Ok(None)
}
fn supports_async_io(&self) -> bool {
cfg!(target_os = "windows")
}
fn name(&self) -> &'static str {
"iocp"
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iocp_creation() {
let result = IocpReactor::new(256);
assert!(result.is_ok());
}
#[test]
fn test_iocp_name() {
let reactor = IocpReactor::new(256).unwrap();
assert_eq!(reactor.name(), "iocp");
}
#[test]
fn test_iocp_register_deregister() {
let mut reactor = IocpReactor::new(256).unwrap();
let token = reactor.register(42, Interest::READABLE).unwrap();
assert_eq!(token, Token(1));
assert!(reactor.modify(token, Interest::BOTH).is_ok());
assert!(reactor.deregister(token).is_ok());
assert!(reactor.modify(token, Interest::READABLE).is_err());
}
#[test]
fn test_iocp_submit_operations() {
let mut reactor = IocpReactor::new(256).unwrap();
let token = reactor.register(42, Interest::BOTH).unwrap();
let mut buf = [0u8; 1024];
let result = reactor.submit_read(token, &mut buf);
assert!(result.is_ok());
let data = b"test data";
let result = reactor.submit_write(token, data);
assert!(result.is_ok());
let bad_token = Token(999);
assert!(reactor.submit_read(bad_token, &mut buf).is_err());
}
#[test]
fn test_iocp_availability() {
let available = IocpReactor::is_available();
#[cfg(target_os = "windows")]
assert!(available);
#[cfg(not(target_os = "windows"))]
assert!(!available);
}
}