Skip to content

Tracking Issue for unix_sigpipe #97889

Open

Description

@Enselic

View all comments




↓↓↓↓ Important ↓↓↓↓

The ui changed from an attribute to a compiler flag: #124480, so the below description is out of date. Someone (maybe me) should update the description.

↑↑↑↑ Important ↑↑↑↑




The feature gate for the issue is #![feature(unix_sigpipe)].
It enables a new fn main() attribute #[unix_sigpipe = "..."].

Usage

Any simple Rust program that writes a sizeable amount of data to stdout will panic if its output is limited via pipes.

fn main() {
    loop {
        println!("hello world");
    }
}
% ./main | head -n 1
hello world
thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', library/std/src/io/stdio.rs:1016:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrac

To prevent panicking we can use the new attribute:

#![feature(unix_sigpipe)]

#[unix_sigpipe = "sig_dfl"]
fn main() {
    loop {
        println!("hello world");
    }
}
% ./main | head -n 1
hello world

More Info

Please refer to the unstable book section for more details. In short:

#[unix_sigpipe = "..."] Behaviour
sig_ign Set SIGPIPE handler to SIG_IGN before invoking fn main(). Default behaviour since 2014.
sig_dfl Set SIGPIPE handler to SIG_DFL before invoking fn main().
inherit Leave SIGPIPE handler untounched before entering fn main().

The problem with the current SIGPIPE code in libstd as well as several other aspects of this problem is discussed extensively at these places:

Naming convention

The naming follows the convention used by #![windows_subsystem = "windows|console"] where the values "windows" and "console" have the same names as the actual linker flags: /SUBSYSTEM:WINDOWS and /SUBSYSTEM:CONSOLE.

The names sig_ign and sig_dfl comes from the signal handler names SIG_IGN and SIG_DFL.

Steps

Unresolved Questions That Blocks Stabilisation

  • How can we make it easy to put fn lang_start() in an external crate that can be compiled with stable?
  • We should rename the attribute and attribute values to things that reflect what they do rather than how they do it.
  • #[unix_sigpipe = "sig_dfl"]
    • None
  • #[unix_sigpipe = "sig_ign"]
  • #[unix_sigpipe = "inherit"]
    • Is the name clear enough? Maybe rename to unchanged?

Unresolved Questions That Does Not Block Stabilisation

Because these questions can be resolved incrementally after stabilization.

Resolved Questions

  • We don't want to change to a -Z unix_sigpipe flag instead, see https://rust-lang.zulipchat.com/#narrow/stream/219381-t-libs/topic/Proposal.3A.20First.20step.20towards.20solving.20the.20SIGPIPE.20problem/near/285499895, at least not initially.
  • Should we only have the third sigpipe: u8 argument to fn lang_start() on Unix platform via cfg?
    Answer: No, this is not allowed, see top level comment in https://github.com/rust-lang/rust/blob/master/src/tools/tidy/src/pal.rs
  • Should we stabilize sig_dfl or is inherit and sig_ign sufficient?
    Answer: There are noteworthy examples of real projects that has opted to use SIG_DFL to solve the BrokenPipe problem. Notably rustc itself. So if we don't stabilize sig_dfl, such projects can't make use of our new attribute. Therefore, we also need to stabilize sig_dfl.
  • Should the attribute go on fn main() or on the top-level module (#![unix_sigpipe="..."])?
    Answer: It makes a lot of semantic sense to have the attribute on fn main(), because it is a way to configure what the Rust runtime should do before fn main() is invoked. For libraries, no entry point code that modifies SIGPIPE is generated, so allowing the attribute in these situations does not make much sense. See Change process spawning to inherit the parent's signal mask by default #101077 (comment) for small-scale discussion.
  • Can we write the code in a way that allows lto to remove the _signal stub code completely? With a bool it works (see Support #[unix_sigpipe = "inherit|sig_dfl"] on fn main() to prevent ignoring SIGPIPE #97802 (comment)), but with the current u8 we might need to do some tweaks. Answer: There are currently 4 values and I see no feasible way to reduce it to 2.
  • Can and should we alter the BrokenPipe error 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.
  • Does this have any impact on defining a stable ABI? Answer: No, because ABI discussion are about enabling things such as calling Rust functions in binary artifacts produced by an older Rust compiler than the current one. That we changed the ABI of fn lang_start() is not relevant. And a stable Rust ABI is not even close (see Define a Rust ABI rfcs#600).
  • Can we use MSG_NOSIGNAL with send() etc instead of setting SIGPIPE globally? Answer: No, because there is no equivalent for write(), and it would incur an extra syscall for each write-operation, which is likely to have significant performance drawbacks.

Disclaimer: I have taken the liberty to mark some questions resolved that I find unlikely to be controversial. If you would like me to create a proper discussion ticket for any of the resolved or unresolved questions, please let me know!

About tracking issues

Tracking issues are used to record the overall progress of implementation. They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions. A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature. Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.

@rustbot label +T-libs

Activity

  1. added
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on Jun 8, 2022
  2. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    on Jun 8, 2022
  3. changed the title [-]Tracking Issue for `no_ignore_sigpipe`[/-] [+]Tracking Issue for `#![sigpipe_handler(sig_ign|unchanged)]`[/+] on Jun 10, 2022
  4. changed the title [-]Tracking Issue for `#![sigpipe_handler(sig_ign|unchanged)]`[/-] [+]Tracking Issue for `pipeable`[/+] on Jun 13, 2022
  5. changed the title [-]Tracking Issue for `pipeable`[/-] [+]Tracking Issue for `sigpipe`[/+] on Jun 16, 2022
  6. changed the title [-]Tracking Issue for `sigpipe`[/-] [+]Tracking Issue for `unix_sigpipe`[/+] on Jun 19, 2022
  7. 78 remaining items

  8. sendittothenewts commented on Nov 4, 2024

    @sendittothenewts

    The mask would be inherited across fork and exec, thus exporting this strangeness to child processes, even non-Rust ones.

    That also happens with disabling the signal rather than masking it, right?

    Yes, I didn't mean to imply that signal dispositions are not inherited. Rather, by switching to masking SIGPIPE instead of setting its disposition to SIG_IGN, the child will still inherit the block, but now in an observably different and more unusual way, which may require the (potentially non-Rust) child to handle the situation differently. For example, using sigaction to install a custom signal handler or reset it to SIG_DFL would no longer work properly. And even if the child does go the extra mile and unmask the signal, it now risks getting killed by a SIGPIPE that was triggered by its parent and has already been duly handled as EPIPE.

    I'd say that @jstarks has already put forward the correct, idiomatically POSIX solution in #97889 (comment)

  9. added
    S-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.
    S-tracking-needs-deep-researchStatus: This feature needs deep research to solve design or implementation issues.
    on Nov 25, 2024
  10. Enselic commented on Mar 2, 2026

    @Enselic
    MemberAuthor

    Status Update 2026-03-02

    Hi, I would like to give a status update on this tracking issue.

    The current implementation is based on a compiler flag. The plan is to remove the compiler flag and instead base an implementation on an Externally Implementable Item.

    This tracking issue will be closed and superseded by #150588 (Note: no discussions in that tracking issue please, use Zulip instead).

    However, I will wait to close this issue until the new implementation works and has been merged. I have a draft PR that replaces -Zon-broken-pipe=... with an Externally Implementable Item #[std::io::on_broken_pipe], but it is currently blocked by #151271, which is a bit tricky to solve. Meanwhile, this tracking issue will remain open and the current implementation will remain in place.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCS-tracking-needs-deep-researchStatus: This feature needs deep research to solve design or implementation issues.S-tracking-needs-documentationStatus: Needs documentation.S-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions