Skip to content

Commit f4d31a6

Browse files
committed
tee: improve stdout performance
1 parent f65350a commit f4d31a6

2 files changed

Lines changed: 35 additions & 18 deletions

File tree

src/uu/tee/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ doctest = false
2121

2222
[dependencies]
2323
clap = { workspace = true }
24-
uucore = { workspace = true, features = ["libc", "parser", "signals"] }
2524
fluent = { workspace = true }
25+
rustix = { workspace = true, features = ["stdio"] }
26+
uucore = { workspace = true, features = ["libc", "parser", "signals"] }
2627

2728
[dev-dependencies]
2829
divan = { workspace = true }

src/uu/tee/src/tee.rs

Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,15 @@
55

66
// spell-checker:ignore espidf nopipe
77

8+
use core::mem::ManuallyDrop;
89
use std::ffi::OsString;
10+
use std::fs::File;
911
use std::fs::OpenOptions;
10-
use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin, stdout};
12+
#[cfg(windows)]
13+
use std::io::stdout;
14+
use std::io::{Error, ErrorKind, Read, Result, Write, stderr, stdin};
15+
#[cfg(windows)]
16+
use std::os::windows::io::{AsRawHandle as _, FromRawHandle as _};
1117
use std::path::PathBuf;
1218
use uucore::display::Quotable;
1319
use uucore::error::{UResult, strip_errno};
@@ -80,7 +86,7 @@ fn tee(options: &Options) -> Result<()> {
8086
0,
8187
NamedWriter {
8288
name: translate!("tee-standard-output").into(),
83-
inner: Writer::Stdout(stdout()),
89+
inner: Writer::stdout_raw(),
8490
},
8591
);
8692

@@ -197,21 +203,16 @@ impl MultiWriter {
197203

198204
fn write_flush(&mut self, buf: &[u8]) -> Result<()> {
199205
let mode = self.output_error_mode;
200-
self.writers.retain_mut(|writer| {
201-
let res = (|| {
202-
writer.inner.write_all(buf)?;
203-
writer.inner.flush()
204-
})();
205-
match res {
206+
self.writers
207+
.retain_mut(|writer| match writer.inner.write_all(buf) {
206208
Ok(()) => true,
207209
Err(e) => {
208210
if let Err(e) = process_error(mode, e, writer, &mut self.ignored_errors) {
209211
self.aborted.get_or_insert(e);
210212
}
211213
false
212214
}
213-
}
214-
});
215+
});
215216
self.aborted.take().map_or(
216217
if self.writers.is_empty() {
217218
// This error kind will never be raised by the standard
@@ -250,23 +251,38 @@ fn process_error(
250251
}
251252

252253
enum Writer {
253-
File(std::fs::File),
254-
Stdout(std::io::Stdout),
254+
File(File),
255+
StdoutRaw(ManuallyDrop<File>),
256+
}
257+
258+
impl Writer {
259+
#[cfg(not(windows))]
260+
fn stdout_raw() -> Self {
261+
// SAFETY: We ensure that the file descriptor is never closed by
262+
// wrapping the `File` in `ManuallyDrop`.
263+
let fd = unsafe { rustix::stdio::take_stdout() };
264+
let f = File::from(fd);
265+
Self::StdoutRaw(ManuallyDrop::new(f))
266+
}
267+
268+
#[cfg(windows)]
269+
fn stdout_raw() -> Self {
270+
let handle = stdout().as_raw_handle();
271+
let f = unsafe { File::from_raw_handle(handle) };
272+
Self::StdoutRaw(ManuallyDrop::new(f))
273+
}
255274
}
256275

257276
impl Write for Writer {
258277
fn write(&mut self, buf: &[u8]) -> Result<usize> {
259278
match self {
260279
Self::File(f) => f.write(buf),
261-
Self::Stdout(s) => s.write(buf),
280+
Self::StdoutRaw(s) => s.write(buf),
262281
}
263282
}
264283

265284
fn flush(&mut self) -> Result<()> {
266-
match self {
267-
Self::File(f) => f.flush(),
268-
Self::Stdout(s) => s.flush(),
269-
}
285+
Ok(())
270286
}
271287
}
272288

0 commit comments

Comments
 (0)