No discussions here please. Use this Zulip topic instead. Or create discussion issues.
Feature gate: #![feature(on_broken_pipe)]
This is a tracking issue for the externally implementable item std::io::on_broken_pipe() -> std::io::OnBrokenPipe that allows programs to affect the SIGPIPE setup code that runs before fn main() is invoked.
Supersedes: #97889 (which will be closed once we have a bare-bones implementation in place.)
Usage
A Rust program that writes a sizeable amount of data to stdout with println!() will panic if its output is piped to a short-lived program:
fn main() {
loop {
println!("hello world");
}
}
% ./main | head
hello world
thread 'main' (3260965) panicked at library/std/src/io/stdio.rs:1165:9:
failed printing to stdout: Broken pipe (os error 32)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
This is because SIGPIPE is changed to SIG_IGN before fn main() is invoked. To prevent panicking, a program can override the new externally implementable item to request that SIGPIPE is not changed before fn main() is invoked. Its disposition will remain SIG_DFL and the program will be killed without error when the pipe is closed:
#![feature(on_broken_pipe)]
/// The standard library asks this function how to setup `SIGPIPE` before `fn main()` is invoked.
/// Here we tell it to inherit `SIGPIPE` from the parent process, which in practice means `SIG_DFL`.
/// This implememtation can also come from an external crate that we link with.
#[std::io::on_broken_pipe]
fn inherit_on_broken_pipe() -> std::io::OnBrokenPipe {
std::io::OnBrokenPipe::Inherit
}
fn main() {
loop {
println!("hello world");
}
}
% ./main | head
hello world
Public API
/// How to change SIGPIPE disposition before `fn main()` is invoked. This lives
/// in `std::io` and is an Externally Implementable Item (eii) that can be
/// overidden by crates even though it is called by std itself.
pub fn on_broken_pipe() -> std::io::OnBrokenPipe {
std::io::OnBrokenPipe::Default
}
/// Specifies what [`ErrorKind::BrokenPipe`] behavior a program should have. In
/// practice this affects the `SIGPIPE` setup code that runs before `fn main()`.
/// Currently only relevant to the `unix` family of operating systems.
#[non_exhaustive] // We want to be able to add more variants later.
pub enum OnBrokenPipe {
/// Set `SIGPIPE` to `SIG_IGN` so that pipe I/O problems are reported as
/// [`ErrorKind::BrokenPipe`] errors. Reset `SIGPIPE` to `SIG_DFL` before
/// child `exec()`.
///
/// Both of these behaviors have been the default since Rust 1.0.
BackwardsCompatible,
/// Set `SIGPIPE` to `SIG_IGN` so that pipe I/O problems kills the process.
/// Don't touch `SIGPIPE` before child `exec()`.
///
/// This is mainly useful when you want programs to terminate when their
/// output is piped to short-lived programs like `head`.
Kill,
/// Set `SIGPIPE` to `SIG_DFL` so that pipe I/O problems are reported as
/// [`ErrorKind::BrokenPipe`] errors. Don't touch `SIGPIPE` before child
/// `exec()`.
Error,
/// Never touch `SIGPIPE`, including before child `exec()`.
/// `SIGPIPE` disposition is always inherited from the parent process.
/// This typically means that programs behave as with [`Self::Kill`].
Inherit,
}
Steps
(Remember to update the S-tracking-* label when checking boxes.)
History
This feature will solve:
This feature was originally implemented as an attribute #[unix_sigpipe = "..."]. It was later changed to a compiler flag -Zon-broken-pipe=.... It is now implemented as an externally implementable item std::io::on_broken_pipe() -> std::io::OnBrokenPipe.
Unresolved Questions
Unresolved Questions That Does Not Block Stabilisation
Because these questions can be resolved after stabilization.
Resolved Questions
No discussions here please. Use this Zulip topic instead. Or create discussion issues.
Feature gate:
#![feature(on_broken_pipe)]This is a tracking issue for the externally implementable item
std::io::on_broken_pipe() -> std::io::OnBrokenPipethat allows programs to affect theSIGPIPEsetup code that runs beforefn main()is invoked.Supersedes: #97889 (which will be closed once we have a bare-bones implementation in place.)
Usage
A Rust program that writes a sizeable amount of data to stdout with
println!()will panic if its output is piped to a short-lived program:This is because
SIGPIPEis changed toSIG_IGNbeforefn main()is invoked. To prevent panicking, a program can override the new externally implementable item to request thatSIGPIPEis not changed beforefn main()is invoked. Its disposition will remainSIG_DFLand the program will be killed without error when the pipe is closed:% ./main | head hello worldPublic API
Steps
missing stability attributeerrors with trivial Externally Implementable Item (eii) function inlibrary/std#150514-Zon-broken-pipe=...with Externally Implementable Item#[std::io::on_broken_pipe]#150591-Zon-broken-pipe=...code.#![feature(extern_item_impls)]implicit from#![feature(on_broken_pipe)]sigpipe: u8fromfn lang_start()instd.-Zon-broken-pipe=...with Externally Implementable Item#[std::io::on_broken_pipe]#150591 (comment).unix_sigpipe#97889 to make sure we cover all aspects of this problem.(Remember to update the
S-tracking-*label when checking boxes.)History
This feature will solve:
This feature was originally implemented as an attribute
#[unix_sigpipe = "..."]. It was later changed to a compiler flag-Zon-broken-pipe=.... It is now implemented as an externally implementable itemstd::io::on_broken_pipe() -> std::io::OnBrokenPipe.Unresolved Questions
OnBrokenPipe::KillandErroror isInheritandDefaultsufficient?#[feature(on_broken_pipe)]without stabilizing#![feature(extern_item_impls)]?Unresolved Questions That Does Not Block Stabilisation
Because these questions can be resolved after stabilization.
SIGPIPE, if we want to do it at all?Resolved Questions
BrokenPipeerror message and make it suggest to use the new attribute? Answer: No, because that would mean we would end up giving developer advice to users that can't act on the advice.MSG_NOSIGNALwithsend()etc instead of settingSIGPIPEglobally? Answer: No, because there is no equivalent forwrite(), and it would incur an extra syscall for each write-operation, which is likely to have significant performance drawbacks.Footnotes
https://std-dev-guide.rust-lang.org/feature-lifecycle/stabilization.html ↩