use std::io;
use std::sync::atomic::Ordering;
use super::super::registration::PlatformUpdateFailure;
#[cfg(any(unix, windows))]
use super::super::registration::PolledEvent;
use super::types::{FdKey, IoReactor};
#[cfg(not(any(unix, windows)))]
use crate::Reactor;
use crate::{Event, Interest, PlatformReactor, RawFd};
impl IoReactor {
#[cfg(not(any(unix, windows)))]
pub(super) fn handle_event(&self, event: Event) -> io::Result<()> {
self.metrics
.events_processed
.fetch_add(1, Ordering::Relaxed);
self.wake_fd_waiters(event)
}
#[cfg(any(unix, windows))]
pub(in crate::reactor) fn handle_polled_event(&self, event: PolledEvent) -> io::Result<()> {
self.metrics
.events_processed
.fetch_add(1, Ordering::Relaxed);
#[cfg(windows)]
if event.was_invalidated() {
return self.wake_invalidated_waiters(event);
}
let readiness = event.event().clone();
self.wake_fd_waiters_if_current(readiness, |platform| {
platform.is_current_polled_event(&event)
})
}
#[cfg(windows)]
fn wake_invalidated_waiters(&self, event: PolledEvent) -> io::Result<()> {
let key = FdKey::from(event.event().fd);
let mut fds = self
.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner());
if !fds.contains_key(&key) {
return Ok(());
}
let mut platform_generations = self
.platform_generations
.lock()
.unwrap_or_else(|poison| poison.into_inner());
if platform_generations.get(&key).copied() != Some(event.generation()) {
return Ok(());
}
platform_generations.remove(&key);
self.waiter_cancellations.clear_interest(key, true, true);
let Some(mut fd_info) = fds.remove(&key) else {
return Ok(());
};
drop(platform_generations);
drop(fds);
if let Some(waker) = fd_info.read_waker.take() {
waker.wake();
}
if let Some(waker) = fd_info.write_waker.take() {
waker.wake();
}
Ok(())
}
#[cfg(any(not(any(unix, windows)), test))]
pub(in crate::reactor) fn wake_fd_waiters(&self, event: Event) -> io::Result<()> {
self.wake_fd_waiters_if_current(event, |_| true)
}
fn wake_fd_waiters_if_current(
&self,
event: Event,
is_current: impl FnOnce(&PlatformReactor) -> bool,
) -> io::Result<()> {
self.wake_fd_waiters_with_platform(event, is_current, |_, fd, interest| {
self.update_platform_registration(fd, interest)
})
}
pub(super) fn update_platform_registration(
&self,
fd: RawFd,
interest: Interest,
) -> Result<(), PlatformUpdateFailure> {
#[cfg(any(unix, windows))]
{
self.platform_reactor.update_registration(fd, interest)
}
#[cfg(not(any(unix, windows)))]
{
self.platform_reactor
.unregister_fd(fd)
.map_err(|error| PlatformUpdateFailure::new(error, None))?;
if interest.readable || interest.writable {
self.platform_reactor
.register_fd(fd, interest)
.map_err(|error| PlatformUpdateFailure::new(error, None))?;
}
Ok(())
}
}
pub(in crate::reactor) fn wake_fd_waiters_with_platform(
&self,
event: Event,
is_current: impl FnOnce(&PlatformReactor) -> bool,
update_platform: impl FnOnce(
&PlatformReactor,
RawFd,
Interest,
) -> Result<(), PlatformUpdateFailure>,
) -> io::Result<()> {
let key = FdKey::from(event.fd);
let mut fds = self
.registered_fds
.lock()
.unwrap_or_else(|poison| poison.into_inner());
let Some(fd_info) = fds.get_mut(&key) else {
return Ok(());
};
if !is_current(&self.platform_reactor) {
return Ok(());
}
fd_info.event_count += 1;
let consume_read =
fd_info.interest.readable && (event.readable || event.error || event.hangup);
let consume_write =
fd_info.interest.writable && (event.writable || event.error || event.hangup);
if !consume_read && !consume_write {
return Ok(());
}
let remaining = Interest {
readable: fd_info.interest.readable && !consume_read,
writable: fd_info.interest.writable && !consume_write,
error: fd_info.interest.error,
};
let platform_result = update_platform(&self.platform_reactor, event.fd, remaining);
let fd_info = fds
.get_mut(&key)
.expect("fd registration remained locked during readiness update");
let read_waker = if consume_read {
#[cfg(any(unix, windows))]
self.waiter_cancellations.clear_interest(key, true, false);
fd_info.read_waker.take()
} else {
None
};
let write_waker = if consume_write {
#[cfg(any(unix, windows))]
self.waiter_cancellations.clear_interest(key, false, true);
fd_info.write_waker.take()
} else {
None
};
let mut stranded_read_waker = None;
let mut stranded_write_waker = None;
let (remove_registration, platform_error) = match platform_result {
Ok(()) => {
if remaining.readable || remaining.writable {
fd_info.interest = remaining;
(false, None)
} else {
(true, None)
}
}
Err(failure) => {
stranded_read_waker = fd_info.read_waker.take();
stranded_write_waker = fd_info.write_waker.take();
if failure.descriptor_closed() {
(true, None)
} else if let Some(armed_interest) = failure.armed_interest() {
fd_info.interest = armed_interest;
(false, Some(failure.into_error()))
} else {
(true, Some(failure.into_error()))
}
}
};
if remove_registration {
#[cfg(windows)]
self.platform_generations
.lock()
.unwrap_or_else(|poison| poison.into_inner())
.remove(&key);
fds.remove(&key);
#[cfg(any(unix, windows))]
self.waiter_cancellations.clear_interest(key, true, true);
}
drop(fds);
if let Some(waker) = read_waker {
waker.wake();
}
if let Some(waker) = write_waker {
waker.wake();
}
if let Some(waker) = stranded_read_waker {
waker.wake();
}
if let Some(waker) = stranded_write_waker {
waker.wake();
}
platform_error.map_or(Ok(()), Err)
}
}