#[cfg(all(
unix,
any(
feature = "tcp",
feature = "udp",
feature = "unix",
feature = "process"
)
))]
use std::os::fd::AsFd as AsSource;
#[cfg(unix)]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd};
#[cfg(all(windows, any(feature = "tcp", feature = "udp")))]
use std::os::windows::io::AsSocket as AsSource;
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, RawSocket};
use std::{
fmt,
io::{self, IoSlice, IoSliceMut, Read, Write},
pin::Pin,
task::{Context, Poll},
};
use futures_io::{AsyncRead, AsyncWrite};
#[cfg(windows)]
use windows_sys::Win32::Networking::WinSock::{
FIONBIO, SOCKET, SOCKET_ERROR, WSAGetLastError, ioctlsocket,
};
use crate::{Interest, Local, Mode, Readiness, Registration, Runtime, Source, mode, reactor};
pub struct AsyncIo<T, M = Local>
where
M: Mode,
{
registration: Registration<M>,
io: M::Ptr<T>,
}
impl<T, M> AsyncIo<T, M>
where
M: Mode,
{
pub fn new(runtime: &Runtime<M>, io: T) -> io::Result<Self>
where
T: Source<M>,
{
set_nonblocking(&io)?;
Self::new_nonblocking(runtime, io)
}
pub fn new_nonblocking(runtime: &Runtime<M>, io: T) -> io::Result<Self>
where
T: Source<M>,
{
let io = M::new_ptr(io);
let registration = reactor::register::<M>(
&runtime.core,
<T as mode::sealed::IntoSource<M>>::source_ptr(io.clone()),
)?;
M::ensure_progress(&runtime.core);
Ok(Self { registration, io })
}
pub fn get_ref(&self) -> &T {
&self.io
}
pub fn into_inner(self) -> T {
let Self { registration, io } = self;
drop(registration);
M::into_inner(io)
}
pub fn readable(&self) -> Readiness<'_, M> {
self.registration.ready(Interest::Readable)
}
pub fn writable(&self) -> Readiness<'_, M> {
self.registration.ready(Interest::Writable)
}
pub async fn read_with<R>(
&self,
mut operation: impl FnMut(&T) -> io::Result<R>,
) -> io::Result<R> {
loop {
match operation(&self.io) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {}
result => return result,
}
self.readable().await?;
}
}
pub async fn write_with<R>(
&self,
mut operation: impl FnMut(&T) -> io::Result<R>,
) -> io::Result<R> {
loop {
match operation(&self.io) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => continue,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {}
result => return result,
}
self.writable().await?;
}
}
pub fn poll_read_with<R>(
&self,
cx: &mut Context<'_>,
operation: impl FnMut(&T) -> io::Result<R>,
) -> Poll<io::Result<R>> {
self.poll_io(cx, Interest::Readable, operation)
}
pub fn poll_write_with<R>(
&self,
cx: &mut Context<'_>,
operation: impl FnMut(&T) -> io::Result<R>,
) -> Poll<io::Result<R>> {
self.poll_io(cx, Interest::Writable, operation)
}
#[cfg(any(
feature = "tcp",
feature = "udp",
all(feature = "unix", unix),
all(feature = "process", unix)
))]
pub(crate) fn from_nonblocking(runtime: &Runtime<M>, io: T) -> io::Result<Self>
where
T: AsSource + Send + Sync + 'static,
{
let io = M::new_ptr(io);
let registration = reactor::register::<M>(&runtime.core, M::source_ptr(io.clone()))?;
M::ensure_progress(&runtime.core);
Ok(Self { registration, io })
}
#[cfg(any(
feature = "tcp",
all(feature = "unix", unix),
all(
feature = "process",
any(
target_vendor = "apple",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd",
target_os = "dragonfly"
)
)
))]
pub(crate) fn runtime(&self) -> Runtime<M> {
self.registration.runtime()
}
fn poll_io<R>(
&self,
cx: &mut Context<'_>,
interest: Interest,
mut operation: impl FnMut(&T) -> io::Result<R>,
) -> Poll<io::Result<R>> {
let io = &*self.io;
self.registration.poll_io(cx, interest, || {
loop {
match operation(io) {
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
result => return result,
}
}
})
}
}
impl<T, M> fmt::Debug for AsyncIo<T, M>
where
M: Mode,
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AsyncIo")
.field("io", self.get_ref())
.finish_non_exhaustive()
}
}
#[cfg(unix)]
impl<T, M> AsFd for AsyncIo<T, M>
where
M: Mode,
T: AsFd,
{
fn as_fd(&self) -> BorrowedFd<'_> {
self.get_ref().as_fd()
}
}
#[cfg(unix)]
impl<T, M> AsRawFd for AsyncIo<T, M>
where
M: Mode,
T: AsRawFd,
{
fn as_raw_fd(&self) -> RawFd {
self.get_ref().as_raw_fd()
}
}
#[cfg(windows)]
impl<T, M> AsSocket for AsyncIo<T, M>
where
M: Mode,
T: AsSocket,
{
fn as_socket(&self) -> BorrowedSocket<'_> {
self.get_ref().as_socket()
}
}
#[cfg(windows)]
impl<T, M> AsRawSocket for AsyncIo<T, M>
where
M: Mode,
T: AsRawSocket,
{
fn as_raw_socket(&self) -> RawSocket {
self.get_ref().as_raw_socket()
}
}
impl<T, M> AsyncRead for AsyncIo<T, M>
where
M: Mode,
for<'a> &'a T: Read,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_read(cx, buf)
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_read_vectored(cx, bufs)
}
}
impl<T, M> AsyncWrite for AsyncIo<T, M>
where
M: Mode,
for<'a> &'a T: Write,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_write(cx, buf)
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
Pin::new(&mut &*self).poll_write_vectored(cx, bufs)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &*self).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut &*self).poll_close(cx)
}
}
impl<T, M> AsyncRead for &AsyncIo<T, M>
where
M: Mode,
for<'a> &'a T: Read,
{
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
self.poll_read_with(cx, |mut io| io.read(buf))
}
fn poll_read_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> {
self.poll_read_with(cx, |mut io| io.read_vectored(bufs))
}
}
impl<T, M> AsyncWrite for &AsyncIo<T, M>
where
M: Mode,
for<'a> &'a T: Write,
{
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.poll_write_with(cx, |mut io| io.write(buf))
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[IoSlice<'_>],
) -> Poll<io::Result<usize>> {
self.poll_write_with(cx, |mut io| io.write_vectored(bufs))
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_write_with(cx, |mut io| io.flush())
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}
#[cfg(unix)]
fn set_nonblocking<T>(io: &T) -> io::Result<()>
where
T: AsFd,
{
Ok(rustix::io::ioctl_fionbio(io, true)?)
}
#[cfg(windows)]
fn set_nonblocking<T>(io: &T) -> io::Result<()>
where
T: AsSocket,
{
let mut enable = 1u32;
let result = unsafe {
ioctlsocket(
io.as_socket().as_raw_socket() as SOCKET,
FIONBIO,
&mut enable,
)
};
if result == SOCKET_ERROR {
return Err(io::Error::from_raw_os_error(unsafe { WSAGetLastError() }));
}
Ok(())
}