pub(crate) const LINGER: Duration = Duration::from_secs(60);
use core::fmt;
use core::mem::ManuallyDrop;
use core::ops::{Deref, DerefMut};
use std::io;
#[cfg(unix)]
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{
AsHandle, AsRawHandle, AsRawSocket, AsSocket, BorrowedHandle, BorrowedSocket, IntoRawHandle,
IntoRawSocket, RawHandle, RawSocket,
};
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use super::raw::Raw;
use super::source::{Kind, Source};
use super::sys;
use crate::fs::file::FileIo;
mod sealed {
pub trait Sealed {}
}
pub trait IoObject: sealed::Sealed {
#[doc(hidden)]
fn raw_io(&self) -> Raw;
}
#[cfg(unix)]
impl<T: AsFd> sealed::Sealed for T {}
#[cfg(unix)]
impl<T: AsFd> IoObject for T {
fn raw_io(&self) -> Raw {
self.as_fd().as_raw_fd()
}
}
macro_rules! windows_sockets {
($($ty:ty),*) => {$(
#[cfg(windows)]
impl sealed::Sealed for $ty {}
#[cfg(windows)]
impl IoObject for $ty {
fn raw_io(&self) -> Raw {
self.as_raw_socket()
}
}
)*};
}
windows_sockets!(
std::net::TcpStream,
std::net::TcpListener,
std::net::UdpSocket
);
macro_rules! windows_handles {
($($ty:ty),*) => {$(
#[cfg(windows)]
impl sealed::Sealed for $ty {}
#[cfg(windows)]
impl IoObject for $ty {
fn raw_io(&self) -> Raw {
self.as_raw_handle() as usize as Raw
}
}
)*};
}
windows_handles!(
std::fs::File,
std::process::ChildStdin,
std::process::ChildStdout,
std::process::ChildStderr
);
pub struct Handle<T> {
io: ManuallyDrop<T>,
pub(crate) source: Arc<Source>,
pub(crate) file: OnceLock<Arc<FileIo>>,
}
impl<T: IoObject> Handle<T> {
pub fn new(io: T) -> io::Result<Handle<T>> {
let kind = sys::kind_of(io.raw_io())?;
Self::adopt(io, kind)
}
pub(crate) fn adopt(io: T, kind: Kind) -> io::Result<Handle<T>> {
#[cfg(unix)]
{
let fd = io.raw_io();
sys::set_nonblocking(fd)?;
if !matches!(kind, Kind::Bytes | Kind::File) {
sys::set_nosigpipe(fd);
}
}
Ok(Self::from_nonblocking(io, kind))
}
pub(crate) fn from_nonblocking(io: T, kind: Kind) -> Handle<T> {
let source = Source::new(io.raw_io(), kind);
Handle {
io: ManuallyDrop::new(io),
source,
file: OnceLock::new(),
}
}
pub(crate) fn file(&self) -> io::Result<&Arc<FileIo>> {
if let Some(file) = self.file.get() {
return Ok(file);
}
let file = FileIo::new(self.io.raw_io())?;
Ok(self.file.get_or_init(|| file))
}
pub(crate) fn source(&self) -> &Arc<Source> {
if self.source.is_retired() {
self.source.revive(self.io.raw_io());
}
&self.source
}
}
impl<T> Handle<T> {
pub fn get_ref(&self) -> &T {
&self.io
}
pub fn get_mut(&mut self) -> &mut T {
self.deliver_unsent();
self.hand_file_back();
self.source.retire();
&mut self.io
}
pub fn into_inner(mut self) -> T {
self.deliver_unsent();
self.hand_file_back();
let mut this = ManuallyDrop::new(self);
this.source.retire();
unsafe {
drop(core::ptr::read(&this.source));
drop(core::ptr::read(&this.file));
ManuallyDrop::take(&mut this.io)
}
}
fn hand_file_back(&mut self) {
if let Some(file) = self.file.take()
&& let Ok(fd) = self.source.raw_fd()
{
let _ = sys::seek(fd, Some(file.cursor()));
}
}
}
impl<T> Handle<T> {
fn deliver_unsent(&self) {
if self.source.has_unsent() {
let source = &self.source;
let _ = crate::sync::internal::blocking(crate::time::timeout(
LINGER,
core::future::poll_fn(|cx| source.poll_drain(cx)),
));
}
if let Some(file) = self.file.get().filter(|f| f.has_unsent()) {
let _ = crate::sync::internal::blocking(crate::time::timeout(
LINGER,
core::future::poll_fn(|cx| file.poll_flush(cx)),
));
}
}
}
impl<T> Drop for Handle<T> {
fn drop(&mut self) {
if let Some(file) = self.file.take().filter(|f| f.has_unsent()) {
crate::runtime::spawn(async move {
let _ =
crate::time::timeout(LINGER, core::future::poll_fn(|cx| file.poll_flush(cx)))
.await;
})
.detach();
}
let io = unsafe { ManuallyDrop::take(&mut self.io) };
#[cfg(unix)]
match self
.source
.has_unsent()
.then(|| self.source.duplicate())
.flatten()
{
Some(copy) => {
self.source.close(io, Some(copy.as_raw_fd()));
crate::runtime::spawn(linger(Arc::clone(&self.source), copy)).detach();
}
None => self.source.close(io, None),
}
#[cfg(windows)]
match self
.source
.raw_fd()
.ok()
.filter(|_| self.source.has_unsent())
{
Some(socket) => {
self.source.close((), Some(socket));
let kept = Kept::new(io);
crate::runtime::spawn(linger(Arc::clone(&self.source), kept)).detach();
}
None => self.source.close(io, None),
}
}
}
async fn linger<C: Send + 'static>(source: Arc<Source>, copy: C) {
let _ = crate::time::timeout(LINGER, core::future::poll_fn(|cx| source.poll_drain(cx))).await;
source.close(copy, None);
}
#[cfg(windows)]
struct Kept {
ptr: core::ptr::NonNull<()>,
drop: unsafe fn(core::ptr::NonNull<()>),
}
#[cfg(windows)]
unsafe impl Send for Kept {}
#[cfg(windows)]
impl Kept {
fn new<T>(io: T) -> Self {
unsafe fn drop_boxed<T>(ptr: core::ptr::NonNull<()>) {
drop(unsafe { Box::from_raw(ptr.cast::<T>().as_ptr()) });
}
Self {
ptr: core::ptr::NonNull::from(Box::leak(Box::new(io))).cast(),
drop: drop_boxed::<T>,
}
}
}
#[cfg(windows)]
impl Drop for Kept {
fn drop(&mut self) {
unsafe { (self.drop)(self.ptr) };
}
}
impl<T> Deref for Handle<T> {
type Target = T;
fn deref(&self) -> &T {
&self.io
}
}
impl<T> DerefMut for Handle<T> {
fn deref_mut(&mut self) -> &mut T {
self.get_mut()
}
}
impl<T: fmt::Debug> fmt::Debug for Handle<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Handle").field("io", &*self.io).finish()
}
}
#[cfg(unix)]
impl<T: AsFd> AsFd for Handle<T> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.io.as_fd()
}
}
#[cfg(unix)]
impl<T: AsRawFd> AsRawFd for Handle<T> {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}
#[cfg(unix)]
impl<T: IntoRawFd> IntoRawFd for Handle<T> {
fn into_raw_fd(self) -> RawFd {
self.into_inner().into_raw_fd()
}
}
#[cfg(windows)]
impl<T: AsSocket> AsSocket for Handle<T> {
fn as_socket(&self) -> BorrowedSocket<'_> {
self.io.as_socket()
}
}
#[cfg(windows)]
impl<T: AsRawSocket> AsRawSocket for Handle<T> {
fn as_raw_socket(&self) -> RawSocket {
self.io.as_raw_socket()
}
}
#[cfg(windows)]
impl<T: IntoRawSocket> IntoRawSocket for Handle<T> {
fn into_raw_socket(self) -> RawSocket {
self.into_inner().into_raw_socket()
}
}
#[cfg(windows)]
impl<T: AsHandle> AsHandle for Handle<T> {
fn as_handle(&self) -> BorrowedHandle<'_> {
self.io.as_handle()
}
}
#[cfg(windows)]
impl<T: AsRawHandle> AsRawHandle for Handle<T> {
fn as_raw_handle(&self) -> RawHandle {
self.io.as_raw_handle()
}
}
#[cfg(windows)]
impl<T: IntoRawHandle> IntoRawHandle for Handle<T> {
fn into_raw_handle(self) -> RawHandle {
self.into_inner().into_raw_handle()
}
}