Skip to content

Commit b9b649a

Browse files
committed
cat: avoid pipe() if stdout is pipe
1 parent 11e1310 commit b9b649a

File tree

2 files changed

+33
-26
lines changed

2 files changed

+33
-26
lines changed

src/uu/cat/src/splice.rs

Lines changed: 30 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -23,33 +23,40 @@ pub(super) fn write_fast_using_splice<R: FdReadable, S: AsRawFd + AsFd>(
2323
handle: &InputHandle<R>,
2424
write_fd: &S,
2525
) -> CatResult<bool> {
26-
use nix::fcntl::{FcntlArg, fcntl};
27-
let (pipe_rd, pipe_wr) = pipe()?;
28-
// improve performance
29-
let _ = fcntl(
30-
write_fd,
31-
FcntlArg::F_SETPIPE_SZ(MAX_ROOTLESS_PIPE_SIZE as i32),
32-
);
33-
34-
loop {
35-
match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) {
36-
Ok(n) => {
37-
if n == 0 {
38-
return Ok(false);
26+
const FIRST_PIPE_SIZE: usize = 64 * 1024;
27+
if splice(&handle.reader, &write_fd, FIRST_PIPE_SIZE).is_ok() {
28+
// fcntl improves performance for large file which is large overhead for small files
29+
let _ = rustix::pipe::fcntl_setpipe_size(write_fd, MAX_ROOTLESS_PIPE_SIZE);
30+
loop {
31+
match splice(&handle.reader, &write_fd, MAX_ROOTLESS_PIPE_SIZE) {
32+
Ok(1..) => {}
33+
Ok(0) => return Ok(false),
34+
Err(_) => return Ok(true),
35+
}
36+
}
37+
} else {
38+
// output is not pipe. Needs broker to use splice() which is high cost for small files
39+
let (pipe_rd, pipe_wr) = pipe()?;
40+
loop {
41+
match splice(&handle.reader, &pipe_wr, MAX_ROOTLESS_PIPE_SIZE) {
42+
Ok(n) => {
43+
if n == 0 {
44+
return Ok(false);
45+
}
46+
if splice_exact(&pipe_rd, write_fd, n).is_err() {
47+
// If the first splice manages to copy to the intermediate
48+
// pipe, but the second splice to stdout fails for some reason
49+
// we can recover by copying the data that we have from the
50+
// intermediate pipe to stdout using normal read/write. Then
51+
// we tell the caller to fall back.
52+
copy_exact(&pipe_rd, write_fd, n)?;
53+
return Ok(true);
54+
}
3955
}
40-
if splice_exact(&pipe_rd, write_fd, n).is_err() {
41-
// If the first splice manages to copy to the intermediate
42-
// pipe, but the second splice to stdout fails for some reason
43-
// we can recover by copying the data that we have from the
44-
// intermediate pipe to stdout using normal read/write. Then
45-
// we tell the caller to fall back.
46-
copy_exact(&pipe_rd, write_fd, n)?;
56+
Err(_) => {
4757
return Ok(true);
4858
}
4959
}
50-
Err(_) => {
51-
return Ok(true);
52-
}
5360
}
5461
}
5562
}

src/uucore/src/lib/features/pipes.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
//! Thin zero-copy-related wrappers around functions from the `rustix` crate.
77
8+
#[cfg(any(target_os = "linux", target_os = "android"))]
9+
use rustix::pipe::{SpliceFlags, fcntl_setpipe_size};
810
#[cfg(any(target_os = "linux", target_os = "android"))]
911
use std::fs::File;
1012
#[cfg(any(target_os = "linux", target_os = "android"))]
1113
use std::os::fd::AsFd;
12-
#[cfg(any(target_os = "linux", target_os = "android"))]
13-
use rustix::pipe::SpliceFlags;
1414
pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
1515

1616
/// A wrapper around [`rustix::pipe::pipe`] that ensures the pipe is cleaned up.
@@ -22,7 +22,7 @@ pub const MAX_ROOTLESS_PIPE_SIZE: usize = 1024 * 1024;
2222
pub fn pipe() -> std::io::Result<(File, File)> {
2323
let (read, write) = rustix::pipe::pipe()?;
2424
// improve performance for splice
25-
let _ = fcntl(&read, FcntlArg::F_SETPIPE_SZ(MAX_ROOTLESS_PIPE_SIZE as i32));
25+
let _ = fcntl_setpipe_size(&read, MAX_ROOTLESS_PIPE_SIZE);
2626

2727
Ok((File::from(read), File::from(write)))
2828
}

0 commit comments

Comments
 (0)