Skip to content
Closed
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 src/uu/yes/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ doctest = false
clap = { workspace = true }
itertools = { workspace = true }
fluent = { workspace = true }
rustix = { workspace = true, features = ["pipe"] }
rustix = { workspace = true, features = ["stdio", "pipe"] }
uucore = { workspace = true, features = ["pipes"] }

[[bin]]
Expand Down
16 changes: 9 additions & 7 deletions src/uu/yes/src/yes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,22 +112,24 @@ pub fn exec(mut bytes: Vec<u8>) -> io::Result<()> {

#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn exec(mut bytes: Vec<u8>) -> io::Result<()> {
use uucore::pipes::{pipe, splice, tee};
use uucore::io::RawWriter;
use uucore::pipes::{pipe_exact, splice, tee};

const PAGE_SIZE: usize = 4096;
let aligned = PAGE_SIZE.is_multiple_of(bytes.len());
repeat_content_to_capacity(&mut bytes);
let bytes = bytes.as_slice();
let mut stdout = io::stdout(); // no need to lock with zero-copy
let stdout = rustix::stdio::stdout();
// improve throughput
let _ = rustix::pipe::fcntl_setpipe_size(&stdout, MAX_ROOTLESS_PIPE_SIZE);
let _ = rustix::pipe::fcntl_setpipe_size(stdout, MAX_ROOTLESS_PIPE_SIZE);
// don't show any error from fast-path and fallback to write for proper message
if let Ok((p_read, mut p_write)) = pipe()
// todo: zero-copy with default pipe size is logically possible when fcntl failed
if let Ok((p_read, mut p_write)) = pipe_exact(MAX_ROOTLESS_PIPE_SIZE)
&& p_write.write_all(bytes).is_ok()
{
if aligned && tee(&p_read, &stdout, MAX_ROOTLESS_PIPE_SIZE).is_ok() {
while let Ok(1..) = tee(&p_read, &stdout, MAX_ROOTLESS_PIPE_SIZE) {}
} else if let Ok((broker_read, broker_write)) = pipe() {
} else if let Ok((broker_read, broker_write)) = pipe_exact(MAX_ROOTLESS_PIPE_SIZE) {
// tee() cannot control offset and write to non-pipe
'hybrid: while let Ok(mut remain) = tee(&p_read, &broker_write, MAX_ROOTLESS_PIPE_SIZE)
{
Expand All @@ -137,15 +139,15 @@ pub fn exec(mut bytes: Vec<u8>) -> io::Result<()> {
remain -= s;
} else {
// avoid output breakage with reduced remain even if it would not happen
stdout.write_all(&bytes[bytes.len() - remain..])?;
RawWriter(stdout).write_all(&bytes[bytes.len() - remain..])?;
Comment thread
oech3 marked this conversation as resolved.
break 'hybrid;
}
}
}
}
}
// fallback
let mut stdout = stdout.lock();
let mut stdout = RawWriter(stdout);
loop {
stdout.write_all(bytes)?;
}
Expand Down
21 changes: 18 additions & 3 deletions src/uucore/src/lib/features/pipes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,8 @@
#[cfg(any(target_os = "linux", target_os = "android"))]
use rustix::pipe::{SpliceFlags, fcntl_setpipe_size};
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::fs::File;
#[cfg(any(target_os = "linux", target_os = "android"))]
use std::{
fs::File,
io::{Read, Write},
os::fd::AsFd,
sync::OnceLock,
Expand All @@ -35,20 +34,36 @@ pub fn pipe() -> std::io::Result<(File, File)> {
Ok((File::from(read), File::from(write)))
}

/// return pipe larger than given size and kernel's default size
/// try to return pipe larger than given size
///
/// useful to save RAM usage
#[inline]
#[cfg(any(target_os = "linux", target_os = "android"))]
fn pipe_with_size(s: usize) -> std::io::Result<(File, File)> {
let (read, write) = rustix::pipe::pipe()?;
// avoid unnecessary syscall
if s > KERNEL_DEFAULT_PIPE_SIZE {
let _ = fcntl_setpipe_size(&read, s);
}

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

// used only from yes currently. We might use this for dd...
/// return pipe larger than given size
///
/// use this if writing size to pipe should not hang
#[inline]
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn pipe_exact(s: usize) -> std::io::Result<(File, File)> {
let (read, write) = rustix::pipe::pipe()?;
if s > KERNEL_DEFAULT_PIPE_SIZE {
fcntl_setpipe_size(&read, s)?;
}

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

Comment thread
oech3 marked this conversation as resolved.
/// Less noisy wrapper around [`rustix::pipe::splice`].
///
/// Up to `len` bytes are moved from `source` to `target`. Returns the number
Expand Down
Loading