diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f0a73a308a4a4..84ba197b4be1e 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -684,6 +684,9 @@ pub use core::{ module_path, option_env, stringify, trace_macros, }; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub use crate::sys::anonymous_pipe as pipe; + #[unstable( feature = "concat_bytes", issue = "87555", diff --git a/library/std/src/sys/anonymous_pipe/mod.rs b/library/std/src/sys/anonymous_pipe/mod.rs new file mode 100644 index 0000000000000..ec6d4bfffc23b --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/mod.rs @@ -0,0 +1,144 @@ +//! Module for anonymous pipe +//! +//! ``` +//! #![feature(anonymous_pipe)] +//! +//! # #[cfg(miri)] fn main() {} +//! # #[cfg(not(miri))] +//! # fn main() -> std::io::Result<()> { +//! let (reader, writer) = std::pipe::pipe()?; +//! # Ok(()) +//! # } +//! ``` + +use crate::{io, sys::pipe::AnonPipe}; + +/// Create anonymous pipe that is close-on-exec and blocking. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[inline] +pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + cfg_if::cfg_if! { + if #[cfg(unix)] { + unix::pipe() + } else if #[cfg(windows)] { + windows::pipe() + } else { + Err(io::Error::UNSUPPORTED_PLATFORM) + } + } +} + +/// Read end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeReader(AnonPipe); + +/// Write end of the anonymous pipe. +#[unstable(feature = "anonymous_pipe", issue = "127154")] +#[derive(Debug)] +pub struct PipeWriter(AnonPipe); + +impl PipeReader { + /// Create a new [`PipeReader`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +impl PipeWriter { + /// Create a new [`PipeWriter`] instance that shares the same underlying file description. + #[unstable(feature = "anonymous_pipe", issue = "127154")] + pub fn try_clone(&self) -> io::Result { + self.0.try_clone().map(Self) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for &PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Read for PipeReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + self.0.read_vectored(bufs) + } + #[inline] + fn is_read_vectored(&self) -> bool { + self.0.is_read_vectored() + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> { + self.0.read_buf(buf) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for &PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl io::Write for PipeWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +#[cfg(unix)] +mod unix; + +#[cfg(windows)] +mod windows; + +#[cfg(all(test, not(miri)))] +mod tests; diff --git a/library/std/src/sys/anonymous_pipe/tests.rs b/library/std/src/sys/anonymous_pipe/tests.rs new file mode 100644 index 0000000000000..8f17422901d62 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/tests.rs @@ -0,0 +1,29 @@ +use super::*; +use crate::io::{Read, Write}; + +#[test] +fn pipe_creation_and_rw() { + let (mut rx, mut tx) = pipe().unwrap(); + tx.write_all(b"12345").unwrap(); + drop(tx); + + let mut s = String::new(); + rx.read_to_string(&mut s).unwrap(); + assert_eq!(s, "12345"); +} + +#[test] +fn pipe_try_clone_and_rw() { + let (mut rx, mut tx) = pipe().unwrap(); + tx.try_clone().unwrap().write_all(b"12").unwrap(); + tx.write_all(b"345").unwrap(); + drop(tx); + + let mut s = String::new(); + rx.try_clone().unwrap().take(3).read_to_string(&mut s).unwrap(); + assert_eq!(s, "123"); + + s.clear(); + rx.read_to_string(&mut s).unwrap(); + assert_eq!(s, "45"); +} diff --git a/library/std/src/sys/anonymous_pipe/unix.rs b/library/std/src/sys/anonymous_pipe/unix.rs new file mode 100644 index 0000000000000..833f210dc65b6 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/unix.rs @@ -0,0 +1,108 @@ +use super::*; + +use crate::{ + os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd}, + process::Stdio, + sys::{ + fd::FileDesc, + pipe::{anon_pipe, AnonPipe}, + }, + sys_common::{FromInner, IntoInner}, +}; + +#[inline] +pub(super) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + anon_pipe().map(|(rx, tx)| (PipeReader(rx), PipeWriter(tx))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeReader { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeReader { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeReader) -> Self { + FileDesc::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeReader { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(AnonPipe::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeReader { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsFd for PipeWriter { + fn as_fd(&self) -> BorrowedFd<'_> { + self.0.as_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawFd for PipeWriter { + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedFd { + fn from(pipe: PipeWriter) -> Self { + FileDesc::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawFd for PipeWriter { + unsafe fn from_raw_fd(raw_fd: RawFd) -> Self { + Self(AnonPipe::from_raw_fd(raw_fd)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawFd for PipeWriter { + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedFd::from(pipe)) + } +} + +fn convert_to_pipe(owned_fd: OwnedFd) -> AnonPipe { + AnonPipe::from_inner(FileDesc::from_inner(OwnedFd::from(owned_fd))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_fd: OwnedFd) -> Self { + Self(convert_to_pipe(owned_fd)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_fd: OwnedFd) -> Self { + Self(convert_to_pipe(owned_fd)) + } +} diff --git a/library/std/src/sys/anonymous_pipe/windows.rs b/library/std/src/sys/anonymous_pipe/windows.rs new file mode 100644 index 0000000000000..d5398c3cc9ca8 --- /dev/null +++ b/library/std/src/sys/anonymous_pipe/windows.rs @@ -0,0 +1,114 @@ +use super::*; + +use crate::{ + os::windows::io::{ + AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle, + }, + process::Stdio, + sys::{ + handle::Handle, + pipe::{anon_pipe, AnonPipe, Pipes}, + }, + sys_common::{FromInner, IntoInner}, +}; + +#[inline] +pub(super) fn pipe() -> io::Result<(PipeReader, PipeWriter)> { + anon_pipe(true, false).map(|Pipes { ours, theirs }| (PipeReader(ours), PipeWriter(theirs))) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeReader { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.handle().as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeReader { + fn as_raw_handle(&self) -> RawHandle { + self.0.handle().as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeReader { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(AnonPipe::from_inner(Handle::from_raw_handle(raw_handle))) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeReader { + fn into_raw_handle(self) -> RawHandle { + self.0.into_handle().into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeReader) -> Self { + Handle::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeReader) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsHandle for PipeWriter { + fn as_handle(&self) -> BorrowedHandle<'_> { + self.0.handle().as_handle() + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl AsRawHandle for PipeWriter { + fn as_raw_handle(&self) -> RawHandle { + self.0.handle().as_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl FromRawHandle for PipeWriter { + unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self { + Self(AnonPipe::from_inner(Handle::from_raw_handle(raw_handle))) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl IntoRawHandle for PipeWriter { + fn into_raw_handle(self) -> RawHandle { + self.0.into_handle().into_raw_handle() + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for OwnedHandle { + fn from(pipe: PipeWriter) -> Self { + Handle::into_inner(AnonPipe::into_inner(pipe.0)) + } +} +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for Stdio { + fn from(pipe: PipeWriter) -> Self { + Self::from(OwnedHandle::from(pipe)) + } +} + +fn convert_to_pipe(owned_handle: OwnedHandle) -> AnonPipe { + AnonPipe::from_inner(Handle::from_inner(owned_handle)) +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeReader { + fn from(owned_handle: OwnedHandle) -> Self { + Self(convert_to_pipe(owned_handle)) + } +} + +#[unstable(feature = "anonymous_pipe", issue = "127154")] +impl From for PipeWriter { + fn from(owned_handle: OwnedHandle) -> Self { + Self(convert_to_pipe(owned_handle)) + } +} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index e50758ce00d8b..202997b749513 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -7,6 +7,8 @@ mod pal; mod personality; +#[unstable(feature = "anonymous_pipe", issue = "127154")] +pub mod anonymous_pipe; pub mod backtrace; pub mod cmath; pub mod exit_guard; diff --git a/library/std/src/sys/pal/unix/pipe.rs b/library/std/src/sys/pal/unix/pipe.rs index 33db24e77e4da..8762af614f17e 100644 --- a/library/std/src/sys/pal/unix/pipe.rs +++ b/library/std/src/sys/pal/unix/pipe.rs @@ -9,6 +9,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe(FileDesc); pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { @@ -46,6 +47,10 @@ pub fn anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> { } impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(Self) + } + pub fn read(&self, buf: &mut [u8]) -> io::Result { self.0.read(buf) } @@ -79,6 +84,10 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.0.is_write_vectored() } + + pub fn as_file_desc(&self) -> &FileDesc { + &self.0 + } } impl IntoInner for AnonPipe { diff --git a/library/std/src/sys/pal/unsupported/pipe.rs b/library/std/src/sys/pal/unsupported/pipe.rs index d7d8f297ae586..781eafe2f1a6b 100644 --- a/library/std/src/sys/pal/unsupported/pipe.rs +++ b/library/std/src/sys/pal/unsupported/pipe.rs @@ -1,8 +1,21 @@ -use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; +use crate::{ + fmt, + io::{self, BorrowedCursor, IoSlice, IoSliceMut}, +}; pub struct AnonPipe(!); +impl fmt::Debug for AnonPipe { + fn fmt(&self, _: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0 + } +} + impl AnonPipe { + pub fn try_clone(&self) -> io::Result { + self.0 + } + pub fn read(&self, _buf: &mut [u8]) -> io::Result { self.0 } diff --git a/library/std/src/sys/pal/windows/handle.rs b/library/std/src/sys/pal/windows/handle.rs index aaa1831dcc24d..045ea32bc0a41 100644 --- a/library/std/src/sys/pal/windows/handle.rs +++ b/library/std/src/sys/pal/windows/handle.rs @@ -17,6 +17,7 @@ use crate::sys_common::{AsInner, FromInner, IntoInner}; /// An owned container for `HANDLE` object, closing them on Drop. /// /// All methods are inherited through a `Deref` impl to `RawHandle` +#[derive(Debug)] pub struct Handle(OwnedHandle); impl Handle { diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7a309b9638bd2..3d2ec6a122d9c 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -18,6 +18,7 @@ use crate::sys_common::{FromInner, IntoInner}; // Anonymous pipes //////////////////////////////////////////////////////////////////////////////// +#[derive(Debug)] pub struct AnonPipe { inner: Handle, } @@ -181,7 +182,7 @@ pub fn spawn_pipe_relay( their_handle_inheritable: bool, ) -> io::Result { // We need this handle to live for the lifetime of the thread spawned below. - let source = source.duplicate()?; + let source = source.try_clone()?; // create a new pair of anon pipes. let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?; @@ -237,7 +238,8 @@ impl AnonPipe { pub fn into_handle(self) -> Handle { self.inner } - fn duplicate(&self) -> io::Result { + + pub fn try_clone(&self) -> io::Result { self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner }) }