#[cfg(target_os = "wasi")]
use std::sync::atomic::{AtomicBool, Ordering};
use std::{io, os::fd::BorrowedFd, time::Duration};
use rustix::{
event::{PollFd, PollFlags, Timespec, poll},
io::Errno,
};
#[cfg(target_os = "wasi")]
use super::MAX_TIMEOUT;
#[cfg(unix)]
use super::pipe::Pipe;
use super::{Directions, RawSource, Ready, list::List};
pub(crate) struct Poller {
#[cfg(unix)]
wake: Pipe,
#[cfg(target_os = "wasi")]
woken: AtomicBool,
list: List,
}
impl Poller {
pub(crate) const LIVE: bool = false;
pub(crate) fn new() -> io::Result<Self> {
Ok(Self {
#[cfg(unix)]
wake: Pipe::new()?,
#[cfg(target_os = "wasi")]
woken: AtomicBool::new(false),
list: List::new(),
})
}
pub(crate) fn notify(&self) -> io::Result<()> {
#[cfg(unix)]
{
self.wake.notify()
}
#[cfg(target_os = "wasi")]
{
self.woken.store(true, Ordering::Release);
Ok(())
}
}
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 mut fds = Vec::with_capacity(watched.len() + 1);
for &(_key, descriptor, directions) in &watched {
let mut flags = PollFlags::empty();
if directions.readable {
flags |= PollFlags::IN;
}
if directions.writable {
flags |= PollFlags::OUT;
}
let fd = unsafe { BorrowedFd::borrow_raw(descriptor) };
fds.push(PollFd::from_borrowed_fd(fd, flags));
}
#[cfg(unix)]
fds.push(PollFd::from_borrowed_fd(
self.wake.read_end(),
PollFlags::IN,
));
#[cfg(target_os = "wasi")]
let timeout = Some(if self.woken.swap(false, Ordering::AcqRel) {
Duration::ZERO
} else {
timeout.unwrap_or(MAX_TIMEOUT)
});
let timeout = timeout.and_then(|t| Timespec::try_from(t).ok());
match poll(&mut fds, timeout.as_ref()) {
Ok(_) => {}
Err(Errno::INTR) => return Ok(Vec::new()),
Err(e) => return Err(e.into()),
}
#[cfg(unix)]
if fds[watched.len()].revents().contains(PollFlags::IN) {
self.wake.drain();
}
Ok(watched
.iter()
.zip(&fds)
.filter_map(|(&(key, _, _), fd)| {
let revents = fd.revents();
let hung_up = revents.intersects(PollFlags::ERR | PollFlags::HUP | PollFlags::NVAL);
let directions = Directions {
readable: revents.contains(PollFlags::IN) || hung_up,
writable: revents.contains(PollFlags::OUT) || hung_up,
};
(!directions.is_empty()).then_some(Ready {
key,
directions,
dropped: Directions::NONE,
})
})
.collect())
}
}