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.source().lease()?)?;
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(
crate::rt::linger::time(),
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(
crate::rt::linger::time(),
core::future::poll_fn(|cx| file.poll_flush(cx)),
));
}
}
pub(crate) fn close_now(self) {
let mut this = ManuallyDrop::new(self);
let (io, source, file) = unsafe {
(
ManuallyDrop::take(&mut this.io),
core::ptr::read(&this.source),
core::ptr::read(&this.file),
)
};
drop(file);
source.close(io);
}
}
impl<T> Drop for Handle<T> {
fn drop(&mut self) {
if let Some(file) = self.file.take().filter(|f| f.has_unsent()) {
crate::rt::linger::hand(async move {
let _ = crate::time::timeout(
crate::rt::linger::time(),
core::future::poll_fn(|cx| file.poll_flush(cx)),
)
.await;
});
}
let io = unsafe { ManuallyDrop::take(&mut self.io) };
let Some(own) = self
.source
.raw_fd()
.ok()
.filter(|_| self.source.has_unsent())
else {
self.source.close(io);
return;
};
self.source.linger(own);
crate::rt::linger::hand(linger(Arc::clone(&self.source), Kept::new(io), own));
}
}
async fn linger(source: Arc<Source>, kept: Kept, own: Raw) {
let drain = core::future::poll_fn(|cx| source.poll_drain(cx));
let _ = crate::time::timeout(crate::rt::linger::time(), drain).await;
if source.has_unsent()
&& source.kind() == Kind::Tcp
&& let Err(err) = crate::net::opt::set_linger_time(own, Some(Duration::ZERO))
{
log::warn!(
"a dropped stream's unsent bytes were given up, and its peer is not told: \
the stream could not be reset ({err})"
);
}
source.close(kept);
}
struct Kept {
ptr: core::ptr::NonNull<()>,
drop: unsafe fn(core::ptr::NonNull<()>),
}
unsafe impl Send for Kept {}
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>,
}
}
}
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()
}
}