From f4d31a62c233a335dbe2c5ec216a404f30a500ff Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Sat, 9 May 2026 20:20:29 +0100 Subject: [PATCH] tee: improve stdout performance --- src/uu/tee/Cargo.toml | 3 ++- src/uu/tee/src/tee.rs | 50 ++++++++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/uu/tee/Cargo.toml b/src/uu/tee/Cargo.toml index e52fe7bc022..157badf9829 100644 --- a/src/uu/tee/Cargo.toml +++ b/src/uu/tee/Cargo.toml @@ -21,8 +21,9 @@ doctest = false [dependencies] clap = { workspace = true } -uucore = { workspace = true, features = ["libc", "parser", "signals"] } fluent = { workspace = true } +rustix = { workspace = true, features = ["stdio"] } +uucore = { workspace = true, features = ["libc", "parser", "signals"] } [dev-dependencies] divan = { workspace = true } diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index f4a5876ee4f..1aec23337b2 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -5,9 +5,15 @@ // spell-checker:ignore espidf nopipe +use core::mem::ManuallyDrop; use std::ffi::OsString; +use std::fs::File; use std::fs::OpenOptions; -use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin, stdout}; +#[cfg(windows)] +use std::io::stdout; +use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin}; +#[cfg(windows)] +use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _}; use std::path::PathBuf; use uucore::display::Quotable; use uucore::error::{UResult, strip_errno}; @@ -80,7 +86,7 @@ fn tee(options: &Options) -> Result<()> { 0, NamedWriter { name: translate!("tee-standard-output").into(), - inner: Writer::Stdout(stdout()), + inner: Writer::stdout_raw(), }, ); @@ -197,12 +203,8 @@ impl MultiWriter { fn write_flush(&mut self, buf: &[u8]) -> Result<()> { let mode = self.output_error_mode; - self.writers.retain_mut(|writer| { - let res = (|| { - writer.inner.write_all(buf)?; - writer.inner.flush() - })(); - match res { + self.writers + .retain_mut(|writer| match writer.inner.write_all(buf) { Ok(()) => true, Err(e) => { if let Err(e) = process_error(mode, e, writer, &mut self.ignored_errors) { @@ -210,8 +212,7 @@ impl MultiWriter { } false } - } - }); + }); self.aborted.take().map_or( if self.writers.is_empty() { // This error kind will never be raised by the standard @@ -250,23 +251,38 @@ fn process_error( } enum Writer { - File(std::fs::File), - Stdout(std::io::Stdout), + File(File), + StdoutRaw(ManuallyDrop), +} + +impl Writer { + #[cfg(not(windows))] + fn stdout_raw() -> Self { + // SAFETY: We ensure that the file descriptor is never closed by + // wrapping the `File` in `ManuallyDrop`. + let fd = unsafe { rustix::stdio::take_stdout() }; + let f = File::from(fd); + Self::StdoutRaw(ManuallyDrop::new(f)) + } + + #[cfg(windows)] + fn stdout_raw() -> Self { + let handle = stdout().as_raw_handle(); + let f = unsafe { File::from_raw_handle(handle) }; + Self::StdoutRaw(ManuallyDrop::new(f)) + } } impl Write for Writer { fn write(&mut self, buf: &[u8]) -> Result { match self { Self::File(f) => f.write(buf), - Self::Stdout(s) => s.write(buf), + Self::StdoutRaw(s) => s.write(buf), } } fn flush(&mut self) -> Result<()> { - match self { - Self::File(f) => f.flush(), - Self::Stdout(s) => s.flush(), - } + Ok(()) } }