use std::{io, os::fd::AsRawFd, time::Duration};
use rustix::{
event::{FdSetElement, FdSetIter, Timespec, fd_set_insert, fd_set_num_elements, select},
io::Errno,
};
use super::{Directions, RawSource, Ready, list::List, pipe::Pipe};
pub(crate) struct Poller {
wake: Pipe,
list: List,
}
impl Poller {
pub(crate) const LIVE: bool = false;
pub(crate) fn new() -> io::Result<Self> {
Ok(Self {
wake: Pipe::new()?,
list: List::new(),
})
}
pub(crate) fn notify(&self) -> io::Result<()> {
self.wake.notify()
}
pub(crate) fn add(&self, _key: usize, _descriptor: RawSource) -> io::Result<()> {
self.list.add();
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();
let wake = self.wake.read_end().as_raw_fd();
let nfds = watched
.iter()
.map(|&(_key, descriptor, _directions)| descriptor)
.fold(wake, RawSource::max)
+ 1;
let len = fd_set_num_elements(watched.len() + 1, nfds);
let mut readable = vec![FdSetElement::default(); len];
let mut writable = vec![FdSetElement::default(); len];
fd_set_insert(&mut readable, wake);
for &(_key, descriptor, directions) in &watched {
if directions.readable {
fd_set_insert(&mut readable, descriptor);
}
if directions.writable {
fd_set_insert(&mut writable, descriptor);
}
}
let timeout = timeout.and_then(|t| Timespec::try_from(t).ok());
let found = unsafe {
select(
nfds,
Some(&mut readable),
Some(&mut writable),
None,
timeout.as_ref(),
)
};
match found {
Ok(_) => {}
Err(Errno::INTR) => return Ok(Vec::new()),
Err(e) => return Err(e.into()),
}
let readable = members(&readable);
let writable = members(&writable);
if readable.binary_search(&wake).is_ok() {
self.wake.drain();
}
Ok(watched
.iter()
.filter_map(|&(key, descriptor, _directions)| {
let directions = Directions {
readable: readable.binary_search(&descriptor).is_ok(),
writable: writable.binary_search(&descriptor).is_ok(),
};
(!directions.is_empty()).then_some(Ready {
key,
directions,
dropped: Directions::NONE,
})
})
.collect())
}
}
fn members(set: &[FdSetElement]) -> Vec<RawSource> {
let mut members: Vec<_> = FdSetIter::new(set).collect();
members.sort_unstable();
members
}