use std::io::Read;
use std::net::{TcpListener, TcpStream, UdpSocket};
use std::os::windows::io::AsRawSocket;
use std::sync::Arc;
use std::time::Duration;
use crate::reactor::registration::{PlatformUpdateFailure, RegistrationTable};
use crate::reactor::socket_owner::SocketLease;
use crate::{Interest, RawFd, Reactor};
use super::types::WsaPollReactor;
use super::types::lock_mutex;
#[test]
fn wsapoll_reactor_reports_socket_readiness() {
let reactor = WsaPollReactor::new().expect("reactor");
let recv = UdpSocket::bind("127.0.0.1:0").expect("recv bind");
recv.set_nonblocking(true).expect("nonblocking");
let recv_addr = recv.local_addr().expect("recv addr");
let sender = UdpSocket::bind("127.0.0.1:0").expect("sender bind");
let raw = recv.as_raw_socket() as RawFd;
let raw_key = raw as usize;
reactor
.register_fd(raw, Interest::READABLE)
.expect("register");
let events = reactor
.poll_events(Some(Duration::from_millis(50)))
.expect("poll");
assert!(
events.iter().all(|e| e.fd as usize != raw_key),
"no readiness before data"
);
sender.send_to(b"x", recv_addr).expect("send");
let events = reactor
.poll_events(Some(Duration::from_millis(500)))
.expect("poll");
assert!(
events
.iter()
.any(|e| e.fd as usize == raw_key && e.readable),
"reactor must report the receiver readable, got {events:?}"
);
}
#[test]
fn wsapoll_reactor_wake_interrupts_a_blocking_poll() {
let reactor = Arc::new(WsaPollReactor::new().expect("reactor"));
let polling = Arc::clone(&reactor);
let start = Arc::new(std::sync::Barrier::new(2));
let polling_start = Arc::clone(&start);
let (done_tx, done_rx) = std::sync::mpsc::sync_channel(1);
let handle = std::thread::spawn(move || {
polling_start.wait();
let result = polling.poll_events(Some(Duration::from_secs(5))).map(drop);
done_tx.send(result).expect("report poll completion");
});
start.wait();
reactor.wake().expect("wake");
done_rx
.recv_timeout(Duration::from_secs(2))
.expect("wake must complete the poll before the deadline")
.expect("poll");
handle.join().expect("poll thread");
}
#[test]
fn wsapoll_reactor_self_cleans_closed_sockets() {
let reactor = WsaPollReactor::new().expect("reactor");
let sock = UdpSocket::bind("127.0.0.1:0").expect("bind");
let raw = sock.as_raw_socket() as RawFd;
reactor
.register_fd(raw, Interest::READABLE)
.expect("register");
drop(sock);
let _ = reactor
.poll_events(Some(Duration::from_millis(50)))
.expect("poll must not error on a stale socket");
assert!(
lock_mutex(&reactor.registrations).is_empty(),
"closed socket must be self-cleaned from the interest set"
);
}
#[test]
fn stale_snapshot_cannot_remove_or_match_reused_socket() {
let socket = 41;
let mut registrations = RegistrationTable::default();
let first_generation = registrations.issue_generation().expect("first generation");
registrations.commit(socket, Interest::READABLE, first_generation);
let stale_generation = registrations
.get(socket)
.expect("first registration exists")
.generation;
let replacement_generation = registrations
.issue_generation()
.expect("replacement generation");
registrations.commit(socket, Interest::WRITABLE, replacement_generation);
let current_generation = registrations
.get(socket)
.expect("replacement registration exists")
.generation;
assert_ne!(stale_generation, current_generation);
assert!(!registrations.is_current(socket, stale_generation));
assert!(!registrations.remove_if_current(socket, stale_generation));
assert!(registrations.is_current(socket, current_generation));
}
#[test]
fn owned_snapshot_defers_close_until_kernel_return() {
let reactor = WsaPollReactor::new().expect("reactor");
let mut socket = Some(Arc::new(
UdpSocket::bind("127.0.0.1:0").expect("owned socket bind"),
));
socket
.as_ref()
.expect("owned socket")
.set_nonblocking(true)
.expect("owned socket nonblocking");
let weak = Arc::downgrade(socket.as_ref().expect("owned socket"));
let owner = SocketLease::from(socket.as_ref().expect("owned socket"));
let fd = owner.raw_socket() as RawFd;
reactor
.register_owned_waiter(fd, Interest::READABLE, owner.downgrade())
.map_err(PlatformUpdateFailure::into_error)
.expect("owned waiter registration");
drop(owner);
let events = reactor
.poll_registered_events_after_snapshot(Some(Duration::ZERO), || {
drop(socket.take());
assert!(
weak.upgrade().is_some(),
"snapshot lease must retain the real socket during WSAPoll"
);
})
.expect("owned snapshot poll");
assert!(events.iter().all(|event| event.descriptor() != fd));
assert!(
weak.upgrade().is_none(),
"last snapshot lease must release after WSAPoll"
);
let invalidated = reactor
.poll_registered_events(Some(Duration::ZERO))
.expect("expired owner cleanup");
assert!(
invalidated
.iter()
.any(|event| { event.descriptor() == fd && event.was_invalidated() })
);
assert!(!reactor.has_registration(fd));
}
#[test]
fn tcp_snapshot_defers_peer_eof_until_kernel_return() {
let listener = TcpListener::bind("127.0.0.1:0").expect("listener bind");
let mut peer =
TcpStream::connect(listener.local_addr().expect("listener address")).expect("peer connect");
peer.set_nonblocking(true).expect("peer nonblocking");
let (owned, _) = listener.accept().expect("owned accept");
owned.set_nonblocking(true).expect("owned nonblocking");
let mut owned = Some(Arc::new(owned));
let weak = Arc::downgrade(owned.as_ref().expect("owned stream"));
let owner = SocketLease::from(owned.as_ref().expect("owned stream"));
let fd = owner.raw_socket() as RawFd;
let reactor = WsaPollReactor::new().expect("reactor");
reactor
.register_owned_waiter(fd, Interest::READABLE, owner.downgrade())
.map_err(PlatformUpdateFailure::into_error)
.expect("owned stream registration");
drop(owner);
reactor
.poll_registered_events_after_snapshot(Some(Duration::ZERO), || {
drop(owned.take());
assert!(weak.upgrade().is_some());
let mut byte = [0_u8; 1];
assert_eq!(
peer.read(&mut byte)
.expect_err("snapshot lease must defer peer EOF")
.kind(),
std::io::ErrorKind::WouldBlock
);
})
.expect("owned stream snapshot");
assert!(weak.upgrade().is_none());
peer.set_nonblocking(false).expect("peer blocking mode");
peer.set_read_timeout(Some(Duration::from_secs(2)))
.expect("peer EOF deadline");
let mut byte = [0_u8; 1];
assert_eq!(
peer.read(&mut byte)
.expect("peer EOF after snapshot release"),
0
);
}
#[test]
fn retired_owner_does_not_hide_live_socket_readiness() {
let reactor = WsaPollReactor::new().expect("reactor");
let mut retired = Some(Arc::new(
UdpSocket::bind("127.0.0.1:0").expect("retired socket bind"),
));
let live = Arc::new(UdpSocket::bind("127.0.0.1:0").expect("live socket bind"));
live.set_nonblocking(true).expect("live socket nonblocking");
let retired_owner = SocketLease::from(retired.as_ref().expect("retired socket"));
let retired_fd = retired_owner.raw_socket() as RawFd;
let retired_weak = Arc::downgrade(retired.as_ref().expect("retired socket"));
let live_owner = SocketLease::from(&live);
let live_fd = live_owner.raw_socket() as RawFd;
reactor
.register_owned_waiter(retired_fd, Interest::READABLE, retired_owner.downgrade())
.map_err(PlatformUpdateFailure::into_error)
.expect("retired registration");
reactor
.register_owned_waiter(live_fd, Interest::READABLE, live_owner.downgrade())
.map_err(PlatformUpdateFailure::into_error)
.expect("live registration");
drop(retired_owner);
drop(live_owner);
let sender = UdpSocket::bind("127.0.0.1:0").expect("sender bind");
assert_eq!(
sender
.send_to(b"ready", live.local_addr().expect("live address"))
.expect("readiness datagram"),
5
);
let events = reactor
.poll_registered_events_after_snapshot(Some(Duration::from_secs(1)), || {
drop(retired.take());
assert!(retired_weak.upgrade().is_some());
})
.expect("mixed snapshot poll");
assert!(
events
.iter()
.any(|event| { event.descriptor() == live_fd && event.event().readable })
);
assert!(retired_weak.upgrade().is_none());
let mut payload = [0_u8; 5];
assert_eq!(live.recv(&mut payload).expect("live payload"), 5);
assert_eq!(&payload, b"ready");
}
#[test]
fn snapshot_error_releases_owner_after_unlock() {
let reactor = WsaPollReactor::new().expect("reactor");
let mut socket = Some(Arc::new(
UdpSocket::bind("127.0.0.1:0").expect("owned socket bind"),
));
let weak = Arc::downgrade(socket.as_ref().expect("owned socket"));
let owner = SocketLease::from(socket.as_ref().expect("owned socket"));
let fd = owner.raw_socket() as RawFd;
reactor
.register_owned_waiter(fd, Interest::READABLE, owner.downgrade())
.map_err(PlatformUpdateFailure::into_error)
.expect("owned waiter registration");
drop(owner);
let result = reactor.poll_registered_events_after_snapshot_error(|| {
drop(socket.take());
assert!(weak.upgrade().is_some());
});
let Err(error) = result else {
panic!("unsupported WSAPoll event flags must fail");
};
assert_eq!(error.raw_os_error(), Some(10022));
assert!(
weak.upgrade().is_none(),
"error path releases the final snapshot owner"
);
assert!(reactor.poll_buffer.try_lock().is_ok());
}