Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add stdio #208

Merged
merged 8 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compio-driver/src/iocp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ pub trait AsRawFd {

/// Construct IO objects from raw fds.
pub trait FromRawFd {
/// Constructs a new IO object from the specified raw fd.
/// Constructs an IO object from the specified raw fd.
///
/// # Safety
///
Expand Down
8 changes: 8 additions & 0 deletions compio-fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ compio-driver = { workspace = true }
compio-io = { workspace = true }
compio-runtime = { workspace = true }

cfg-if = { workspace = true }

# Windows specific dependencies
[target.'cfg(windows)'.dependencies]
widestring = { workspace = true }
Expand Down Expand Up @@ -55,3 +57,9 @@ windows-sys = { workspace = true, features = ["Win32_Security_Authorization"] }
# Unix specific dev dependencies
[target.'cfg(unix)'.dev-dependencies]
nix = { workspace = true, features = ["fs"] }

[features]
default = []

read_buf = ["compio-buf/read_buf", "compio-io/read_buf"]
nightly = ["read_buf"]
4 changes: 4 additions & 0 deletions compio-fs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![warn(missing_docs)]
#![cfg_attr(feature = "read_buf", feature(read_buf, core_io_borrowed_buf))]

mod file;
pub use file::*;
Expand All @@ -12,6 +13,9 @@ pub use open_options::*;
mod metadata;
pub use metadata::*;

mod stdio;
pub use stdio::*;

#[cfg(windows)]
pub mod named_pipe;

Expand Down
52 changes: 52 additions & 0 deletions compio-fs/src/stdio/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
cfg_if::cfg_if! {
if #[cfg(windows)] {
mod windows;
pub use windows::*;
} else if #[cfg(unix)] {
mod unix;
pub use unix::*;
}
}

/// Constructs a handle to the standard input of the current process.
///
/// ## Platform specific
/// * Windows: This handle is best used for non-interactive uses, such as when a
/// file is piped into the application. For technical reasons, if `stdin` is a
/// console handle, the read method is implemented by using an ordinary
/// blocking read on a separate thread, and it is impossible to cancel that
/// read. This can make shutdown of the runtime hang until the user presses
/// enter.
///
/// [`AsyncRead`]: compio_io::AsyncRead
pub fn stdin() -> Stdin {
Stdin::new()
}

/// Constructs a handle to the standard output of the current process.
///
/// Concurrent writes to stdout must be executed with care: Only individual
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
///
/// [`AsyncWrite`]: compio_io::AsyncWrite
/// [`write_all`]: compio_io::AsyncWriteExt::write_all
pub fn stdout() -> Stdout {
Stdout::new()
}

/// Constructs a handle to the standard error of the current process.
///
/// Concurrent writes to stderr must be executed with care: Only individual
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
/// you should be aware that writes using [`write_all`] are not guaranteed
/// to occur as a single write, so multiple threads writing data with
/// [`write_all`] may result in interleaved output.
///
/// [`AsyncWrite`]: compio_io::AsyncWrite
/// [`write_all`]: compio_io::AsyncWriteExt::write_all
pub fn stderr() -> Stderr {
Stderr::new()
}
126 changes: 126 additions & 0 deletions compio-fs/src/stdio/unix.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use std::{io, mem::ManuallyDrop};

use compio_buf::{BufResult, IoBuf, IoBufMut, IoVectoredBuf, IoVectoredBufMut};
use compio_driver::{FromRawFd, RawFd};
use compio_io::{AsyncRead, AsyncWrite};
use compio_runtime::TryAsRawFd;

use crate::pipe::{Receiver, Sender};

/// A handle to the standard input stream of a process.
///
/// See [`stdin`].
pub struct Stdin(ManuallyDrop<Receiver>);

impl Stdin {
pub(crate) fn new() -> Self {
// SAFETY: we don't drop it
Self(ManuallyDrop::new(unsafe {
Berrysoft marked this conversation as resolved.
Show resolved Hide resolved
Receiver::from_raw_fd(libc::STDIN_FILENO)
}))
}
}

impl AsyncRead for Stdin {
async fn read<B: IoBufMut>(&mut self, buf: B) -> BufResult<usize, B> {
self.0.read(buf).await
}

async fn read_vectored<V: IoVectoredBufMut>(&mut self, buf: V) -> BufResult<usize, V> {
self.0.read_vectored(buf).await
}
}

impl TryAsRawFd for Stdin {
fn try_as_raw_fd(&self) -> io::Result<RawFd> {
self.0.try_as_raw_fd()
}

unsafe fn as_raw_fd_unchecked(&self) -> RawFd {
self.0.as_raw_fd_unchecked()
}
}

/// A handle to the standard output stream of a process.
///
/// See [`stdout`].
pub struct Stdout(ManuallyDrop<Sender>);

impl Stdout {
pub(crate) fn new() -> Self {
// SAFETY: we don't drop it
Self(ManuallyDrop::new(unsafe {
Sender::from_raw_fd(libc::STDOUT_FILENO)
}))
}
}

impl AsyncWrite for Stdout {
async fn write<T: IoBuf>(&mut self, buf: T) -> BufResult<usize, T> {
self.0.write(buf).await
}

async fn write_vectored<T: IoVectoredBuf>(&mut self, buf: T) -> BufResult<usize, T> {
self.0.write_vectored(buf).await
}

async fn flush(&mut self) -> io::Result<()> {
self.0.flush().await
}

async fn shutdown(&mut self) -> io::Result<()> {
self.0.shutdown().await
}
}

impl TryAsRawFd for Stdout {
fn try_as_raw_fd(&self) -> io::Result<RawFd> {
self.0.try_as_raw_fd()
}

unsafe fn as_raw_fd_unchecked(&self) -> RawFd {
self.0.as_raw_fd_unchecked()
}
}

/// A handle to the standard output stream of a process.
///
/// See [`stderr`].
pub struct Stderr(ManuallyDrop<Sender>);

impl Stderr {
pub(crate) fn new() -> Self {
// SAFETY: we don't drop it
Self(ManuallyDrop::new(unsafe {
Sender::from_raw_fd(libc::STDERR_FILENO)
}))
}
}

impl AsyncWrite for Stderr {
async fn write<T: IoBuf>(&mut self, buf: T) -> BufResult<usize, T> {
self.0.write(buf).await
}

async fn write_vectored<T: IoVectoredBuf>(&mut self, buf: T) -> BufResult<usize, T> {
self.0.write_vectored(buf).await
}

async fn flush(&mut self) -> io::Result<()> {
self.0.flush().await
}

async fn shutdown(&mut self) -> io::Result<()> {
self.0.shutdown().await
}
}

impl TryAsRawFd for Stderr {
fn try_as_raw_fd(&self) -> io::Result<RawFd> {
self.0.try_as_raw_fd()
}

unsafe fn as_raw_fd_unchecked(&self) -> RawFd {
self.0.as_raw_fd_unchecked()
}
}
Loading