use std::io;
use std::os::fd::{AsRawFd, RawFd};
use std::sync::Arc;
use std::time::Instant;
use crate::sync::Mutex;
use tokio::io::Interest as TokioInterest;
use tokio::io::unix::AsyncFd;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Interest {
pub read: bool,
pub write: bool,
}
#[cfg_attr(not(feature = "rustls"), allow(dead_code))]
impl Interest {
pub const READ: Interest = Interest { read: true, write: false };
pub const WRITE: Interest = Interest { read: false, write: true };
}
pub(crate) fn block(fd: RawFd, interest: Interest, deadline: Option<Instant>) -> io::Result<bool> {
let mut events = 0;
if interest.read {
events |= libc::POLLIN;
}
if interest.write {
events |= libc::POLLOUT;
}
loop {
let timeout = match deadline {
None => -1,
Some(d) => {
let now = Instant::now();
if now >= d {
return Ok(false);
}
(d - now).as_millis().clamp(1, i32::MAX as u128) as i32
}
};
let mut pfd = libc::pollfd { fd, events, revents: 0 };
let r = unsafe { libc::poll(&mut pfd, 1, timeout) };
if r < 0 {
let e = io::Error::last_os_error();
if e.kind() == io::ErrorKind::Interrupted {
continue;
}
return Err(e);
}
if r > 0 {
return Ok(true);
}
}
}
#[derive(Clone)]
pub(crate) enum Waitable {
Fd(RawFd),
#[cfg(shuttle)]
Model(Arc<crate::io::model::Pipe>),
}
pub(crate) fn block_on(w: &Waitable, interest: Interest, deadline: Option<Instant>) -> io::Result<bool> {
match w {
Waitable::Fd(fd) => block(*fd, interest, deadline),
#[cfg(shuttle)]
Waitable::Model(pipe) => {
pipe.block(interest);
Ok(true)
}
}
}
#[derive(Debug)]
struct Fd(RawFd);
impl AsRawFd for Fd {
fn as_raw_fd(&self) -> RawFd {
self.0
}
}
#[derive(Default)]
pub(crate) struct Registration(Mutex<Option<Arc<AsyncFd<Fd>>>>);
impl Registration {
fn get(&self, fd: RawFd) -> io::Result<Arc<AsyncFd<Fd>>> {
let mut slot = self.0.lock();
if let Some(afd) = slot.as_ref() {
return Ok(afd.clone());
}
let afd = unsafe { AsyncFd::register_with_interest(Fd(fd), TokioInterest::READABLE | TokioInterest::WRITABLE) };
let afd = Arc::new(afd.map_err(|e| io::Error::other(format!("{e:?}")))?);
*slot = Some(afd.clone());
Ok(afd)
}
pub(crate) fn forget(&self) {
self.0.lock().take();
}
pub(crate) async fn wait_on(&self, w: &Waitable, interest: Interest, deadline: Option<Instant>) -> io::Result<bool> {
match w {
Waitable::Fd(fd) => self.wait(*fd, interest, deadline).await,
#[cfg(shuttle)]
Waitable::Model(pipe) => {
pipe.wait_async(interest).await;
Ok(true)
}
}
}
pub(crate) async fn wait(&self, fd: RawFd, interest: Interest, deadline: Option<Instant>) -> io::Result<bool> {
let wanted = match (interest.read, interest.write) {
(true, true) => TokioInterest::READABLE | TokioInterest::WRITABLE,
(false, true) => TokioInterest::WRITABLE,
_ => TokioInterest::READABLE,
};
for _ in 0..2 {
let afd = self.get(fd)?;
let ready = async {
let mut guard = afd.ready(wanted).await?;
guard.clear_ready();
Ok::<(), io::Error>(())
};
let result = match deadline {
Some(d) => match tokio::time::timeout_at(d.into(), ready).await {
Ok(r) => r,
Err(_) => return Ok(false),
},
None => ready.await,
};
match result {
Ok(()) => return Ok(true),
Err(_) => self.forget(),
}
}
Err(io::Error::other("socket could not be registered with the current tokio runtime"))
}
}