use crate::{EpollFlags, Fd};
use futures_util::FutureExt;
use std::{
io::{Error, ErrorKind},
os::fd::{AsRawFd, BorrowedFd},
pin::Pin,
task::{Poll, ready},
};
pub struct AsyncReadFd<T: AsRawFd> {
inner: Fd<T>,
}
impl<T: AsRawFd> AsyncReadFd<T> {
pub fn new(inner: T) -> Result<Self, std::io::Error> {
Ok(Self {
inner: Fd::new(inner, EpollFlags::EPOLLIN)?,
})
}
}
impl<T: AsRawFd> std::ops::Deref for AsyncReadFd<T> {
type Target = Fd<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: AsRawFd> std::ops::DerefMut for AsyncReadFd<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: AsRawFd + Unpin> futures_io::AsyncRead for AsyncReadFd<T> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &mut [u8],
) -> Poll<std::io::Result<usize>> {
self.inner.poll_with(cx, |fd, _events| {
Ok(nix::unistd::read(
unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) },
buf,
)?)
})
}
}
pub struct AsyncWriteFd<T: AsRawFd> {
inner: Fd<T>,
flush_handle: Option<crate::thread::JoinFuture<std::io::Result<()>>>,
flush_style: Option<FlushStyle>,
}
#[derive(Debug, Clone, Copy)]
enum FlushStyle {
Tcdrain,
Fsync,
}
impl<T: AsRawFd> AsyncWriteFd<T> {
pub fn new(inner: T) -> Result<Self, std::io::Error> {
let fd = unsafe { BorrowedFd::borrow_raw(inner.as_raw_fd()) };
let stat = nix::sys::stat::fstat(fd)?;
let mode = stat.st_mode & nix::libc::S_IFMT;
let flush_style = match mode {
nix::libc::S_IFREG | nix::libc::S_IFBLK | nix::libc::S_IFDIR => Some(FlushStyle::Fsync),
nix::libc::S_IFCHR if nix::unistd::isatty(fd)? => Some(FlushStyle::Tcdrain),
_ => None,
};
Ok(Self {
inner: Fd::new(inner, EpollFlags::EPOLLOUT | EpollFlags::EPOLLET)?,
flush_handle: None,
flush_style,
})
}
}
impl<T: AsRawFd> std::ops::Deref for AsyncWriteFd<T> {
type Target = Fd<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T: AsRawFd> std::ops::DerefMut for AsyncWriteFd<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: AsRawFd + Unpin> futures_io::AsyncWrite for AsyncWriteFd<T> {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
self.inner.poll_with(cx, |fd, _events| {
Ok(nix::unistd::write(
unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) },
buf,
)?)
})
}
fn poll_flush(
mut self: Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<std::io::Result<()>> {
let Some(flush_style) = self.flush_style else {
return Poll::Ready(Ok(()));
};
let borrowed_fd = unsafe { BorrowedFd::borrow_raw(self.inner.inner().as_raw_fd()) };
let flush_handle = self.flush_handle.get_or_insert_with(|| {
let fd = borrowed_fd.try_clone_to_owned();
crate::thread::spawn(move || {
let fd = fd?;
match flush_style {
FlushStyle::Tcdrain => nix::sys::termios::tcdrain(fd)?,
FlushStyle::Fsync => nix::unistd::fsync(fd)?,
}
Ok(())
})
.join()
});
let ret = ready!(flush_handle.poll_unpin(cx)).unwrap();
self.flush_handle = None;
Poll::Ready(ret)
}
fn poll_close(
self: Pin<&mut Self>,
_: &mut std::task::Context<'_>,
) -> Poll<std::io::Result<()>> {
Poll::Ready(Err(Error::new(
ErrorKind::Unsupported,
"epox doesn't support poll_close yet",
)))
}
}