Change `SIGPIPE` ui from `#[unix_sigpipe = "..."]` to `-Zon-broken-pipe=...`

In the stabilization attempt of `#[unix_sigpipe = "sig_dfl"]`, a concern
was raised related to using a language attribute for the feature: Long
term, we want `fn lang_start()` to be definable by any crate, not just
libstd. Having a special language attribute in that case becomes
awkward.

So as a first step towards towards the next stabilization attempt, this
PR changes the `#[unix_sigpipe = "..."]` attribute to a compiler flag
`-Zon-broken-pipe=...` to remove that concern, since now the language
is not "contaminated" by this feature.

Another point was also raised, namely that the ui should not leak
**how** it does things, but rather what the **end effect** is. The new
flag uses the proposed naming. This is of course something that can be
iterated on further before stabilization.
This commit is contained in:
Martin Nordholts 2024-04-28 18:02:21 +02:00
parent e27af2917b
commit cde0cde151
64 changed files with 203 additions and 372 deletions

View File

@ -1,5 +1,3 @@
#![feature(unix_sigpipe)]
// A note about jemalloc: rustc uses jemalloc when built for CI and
// distribution. The obvious way to do this is with the `#[global_allocator]`
// mechanism. However, for complicated reasons (see
@ -34,7 +32,6 @@
// https://github.com/rust-lang/rust/commit/b90cfc887c31c3e7a9e6d462e2464db1fe506175#diff-43914724af6e464c1da2171e4a9b6c7e607d5bc1203fa95c0ab85be4122605ef
// for an example of how to do so.
#[unix_sigpipe = "sig_dfl"]
fn main() {
// See the comment at the top of this file for an explanation of this.
#[cfg(feature = "jemalloc-sys")]

View File

@ -396,10 +396,6 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// Entry point:
gated!(
unix_sigpipe, Normal, template!(NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing,
EncodeCrossCrate::Yes, experimental!(unix_sigpipe)
),
ungated!(start, Normal, template!(Word), WarnFollowing, EncodeCrossCrate::No),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing, EncodeCrossCrate::No),

View File

@ -619,8 +619,6 @@ declare_features! (
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
(unstable, unix_sigpipe, "1.65.0", Some(97889)),
/// Allows unnamed fields of struct and union type
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
/// Allows unsized fn parameters.

View File

@ -20,7 +20,7 @@ use rustc_span::source_map::{RealFileLoader, SourceMapInputs};
use rustc_span::symbol::sym;
use rustc_span::{FileName, SourceFileHashAlgorithm};
use rustc_target::spec::{
CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi,
CodeModel, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, WasmCAbi,
};
use rustc_target::spec::{RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TlsModel};
use std::collections::{BTreeMap, BTreeSet};
@ -809,6 +809,7 @@ fn test_unstable_options_tracking_hash() {
tracked!(no_profiler_runtime, true);
tracked!(no_trait_vptr, true);
tracked!(no_unique_section_names, true);
tracked!(on_broken_pipe, OnBrokenPipe::Kill);
tracked!(oom, OomStrategy::Panic);
tracked!(osx_rpath_install_name, true);
tracked!(packed_bundled_libs, true);

View File

@ -695,9 +695,6 @@ passes_transparent_incompatible =
passes_undefined_naked_function_abi =
Rust ABI is unsupported in naked functions
passes_unix_sigpipe_values =
valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
passes_unknown_external_lang_item =
unknown external lang item: `{$lang_item}`

View File

@ -2522,7 +2522,6 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
sym::automatically_derived,
sym::start,
sym::rustc_main,
sym::unix_sigpipe,
sym::derive,
sym::test,
sym::test_case,

View File

@ -12,8 +12,7 @@ use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
use crate::errors::{
AttrOnlyInFunctions, AttrOnlyOnMain, AttrOnlyOnRootMain, ExternMain, MultipleRustcMain,
MultipleStartFunctions, NoMainErr, UnixSigpipeValues,
AttrOnlyInFunctions, ExternMain, MultipleRustcMain, MultipleStartFunctions, NoMainErr,
};
struct EntryContext<'tcx> {
@ -67,11 +66,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
ctxt.tcx.opt_item_name(id.owner_id.to_def_id()),
);
match entry_point_type {
EntryPointType::None => {
if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
ctxt.tcx.dcx().emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
}
}
EntryPointType::None => (),
_ if !matches!(ctxt.tcx.def_kind(id.owner_id), DefKind::Fn) => {
for attr in [sym::start, sym::rustc_main] {
if let Some(span) = attr_span_by_symbol(ctxt, id, attr) {
@ -81,9 +76,6 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
}
EntryPointType::MainNamed => (),
EntryPointType::OtherMain => {
if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
ctxt.tcx.dcx().emit_err(AttrOnlyOnRootMain { span, attr: sym::unix_sigpipe });
}
ctxt.non_main_fns.push(ctxt.tcx.def_span(id.owner_id));
}
EntryPointType::RustcMainAttr => {
@ -98,9 +90,6 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
}
}
EntryPointType::Start => {
if let Some(span) = attr_span_by_symbol(ctxt, id, sym::unix_sigpipe) {
ctxt.tcx.dcx().emit_err(AttrOnlyOnMain { span, attr: sym::unix_sigpipe });
}
if ctxt.start_fn.is_none() {
ctxt.start_fn = Some((id.owner_id.def_id, ctxt.tcx.def_span(id.owner_id)));
} else {
@ -120,7 +109,7 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
Some((def_id.to_def_id(), EntryFnType::Start))
} else if let Some((local_def_id, _)) = visitor.attr_main_fn {
let def_id = local_def_id.to_def_id();
Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }))
Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }))
} else {
if let Some(main_def) = tcx.resolutions(()).main_def
&& let Some(def_id) = main_def.opt_fn_def_id()
@ -133,31 +122,19 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
return None;
}
return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }));
return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx) }));
}
no_main_err(tcx, visitor);
None
}
}
fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) {
match (attr.value_str(), attr.meta_item_list()) {
(Some(sym::inherit), None) => sigpipe::INHERIT,
(Some(sym::sig_ign), None) => sigpipe::SIG_IGN,
(Some(sym::sig_dfl), None) => sigpipe::SIG_DFL,
(Some(_), None) => {
tcx.dcx().emit_err(UnixSigpipeValues { span: attr.span });
sigpipe::DEFAULT
}
_ => {
// Keep going so that `fn emit_malformed_attribute()` can print
// an excellent error message
sigpipe::DEFAULT
}
}
} else {
sigpipe::DEFAULT
fn sigpipe(tcx: TyCtxt<'_>) -> u8 {
match tcx.sess.opts.unstable_opts.on_broken_pipe {
rustc_target::spec::OnBrokenPipe::Default => sigpipe::DEFAULT,
rustc_target::spec::OnBrokenPipe::Kill => sigpipe::SIG_DFL,
rustc_target::spec::OnBrokenPipe::Error => sigpipe::SIG_IGN,
rustc_target::spec::OnBrokenPipe::Inherit => sigpipe::INHERIT,
}
}

View File

@ -1259,13 +1259,6 @@ pub struct ExternMain {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(passes_unix_sigpipe_values)]
pub struct UnixSigpipeValues {
#[primary_span]
pub span: Span,
}
pub struct NoMainErr {
pub sp: Span,
pub crate_name: Symbol,

View File

@ -2891,7 +2891,9 @@ pub(crate) mod dep_tracking {
use rustc_feature::UnstableFeatures;
use rustc_span::edition::Edition;
use rustc_span::RealFileName;
use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel, WasmCAbi};
use rustc_target::spec::{
CodeModel, MergeFunctions, OnBrokenPipe, PanicStrategy, RelocModel, WasmCAbi,
};
use rustc_target::spec::{
RelroLevel, SanitizerSet, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
};
@ -2955,6 +2957,7 @@ pub(crate) mod dep_tracking {
InstrumentXRay,
CrateType,
MergeFunctions,
OnBrokenPipe,
PanicStrategy,
RelroLevel,
OptLevel,

View File

@ -1,6 +1,6 @@
//! NOTE: Keep these constants in sync with `library/std/src/sys/pal/unix/mod.rs`!
/// The default value if `#[unix_sigpipe]` is not specified. This resolves
/// The default value if `-Zon-broken-pipe=...` is not specified. This resolves
/// to `SIG_IGN` in `library/std/src/sys/pal/unix/mod.rs`.
///
/// Note that `SIG_IGN` has been the Rust default since 2014. See

View File

@ -12,7 +12,7 @@ use rustc_span::edition::Edition;
use rustc_span::RealFileName;
use rustc_span::SourceFileHashAlgorithm;
use rustc_target::spec::{
CodeModel, LinkerFlavorCli, MergeFunctions, PanicStrategy, SanitizerSet, WasmCAbi,
CodeModel, LinkerFlavorCli, MergeFunctions, OnBrokenPipe, PanicStrategy, SanitizerSet, WasmCAbi,
};
use rustc_target::spec::{
RelocModel, RelroLevel, SplitDebuginfo, StackProtector, TargetTriple, TlsModel,
@ -378,6 +378,7 @@ mod desc {
pub const parse_time_passes_format: &str = "`text` (default) or `json`";
pub const parse_passes: &str = "a space-separated list of passes, or `all`";
pub const parse_panic_strategy: &str = "either `unwind` or `abort`";
pub const parse_on_broken_pipe: &str = "either `kill`, `error`, or `inherit`";
pub const parse_opt_panic_strategy: &str = parse_panic_strategy;
pub const parse_oom_strategy: &str = "either `panic` or `abort`";
pub const parse_relro_level: &str = "one of: `full`, `partial`, or `off`";
@ -708,6 +709,17 @@ mod parse {
true
}
pub(crate) fn parse_on_broken_pipe(slot: &mut OnBrokenPipe, v: Option<&str>) -> bool {
match v {
// OnBrokenPipe::Default can't be explicitly specified
Some("kill") => *slot = OnBrokenPipe::Kill,
Some("error") => *slot = OnBrokenPipe::Error,
Some("inherit") => *slot = OnBrokenPipe::Inherit,
_ => return false,
}
true
}
pub(crate) fn parse_oom_strategy(slot: &mut OomStrategy, v: Option<&str>) -> bool {
match v {
Some("panic") => *slot = OomStrategy::Panic,
@ -1839,6 +1851,8 @@ options! {
"do not use unique names for text and data sections when -Z function-sections is used"),
normalize_docs: bool = (false, parse_bool, [TRACKED],
"normalize associated items in rustdoc when generating documentation"),
on_broken_pipe: OnBrokenPipe = (OnBrokenPipe::Default, parse_on_broken_pipe, [TRACKED],
"behavior of std::io::ErrorKind::BrokenPipe (SIGPIPE)"),
oom: OomStrategy = (OomStrategy::Abort, parse_oom_strategy, [TRACKED],
"panic strategy for out-of-memory handling"),
osx_rpath_install_name: bool = (false, parse_bool, [TRACKED],

View File

@ -1935,7 +1935,6 @@ symbols! {
unit,
universal_impl_trait,
unix,
unix_sigpipe,
unlikely,
unmarked_api,
unnamed_fields,

View File

@ -778,6 +778,14 @@ pub enum PanicStrategy {
Abort,
}
#[derive(Clone, Copy, Debug, PartialEq, Hash, Encodable, Decodable, HashStable_Generic)]
pub enum OnBrokenPipe {
Default,
Kill,
Error,
Inherit,
}
impl PanicStrategy {
pub fn desc(&self) -> &str {
match *self {

View File

@ -74,7 +74,7 @@ macro_rules! rtunwrap {
//
// Since 2014, the Rust runtime on Unix has set the `SIGPIPE` handler to
// `SIG_IGN`. Applications have good reasons to want a different behavior
// though, so there is a `#[unix_sigpipe = "..."]` attribute on `fn main()` that
// though, so there is a `-Zon-broken-pipe` compiler flag that
// can be used to select how `SIGPIPE` shall be setup (if changed at all) before
// `fn main()` is called. See <https://github.com/rust-lang/rust/issues/97889>
// for more info.

View File

@ -55,8 +55,8 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
// want!
//
// Hence, we set SIGPIPE to ignore when the program starts up in order
// to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to
// alter this behavior.
// to prevent this problem. Use `-Zon-broken-pipe=...` to alter this
// behavior.
reset_sigpipe(sigpipe);
stack_overflow::init();
@ -194,7 +194,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
_ => unreachable!(),
};
if sigpipe_attr_specified {
UNIX_SIGPIPE_ATTR_SPECIFIED.store(true, crate::sync::atomic::Ordering::Relaxed);
ON_BROKEN_PIPE_FLAG_USED.store(true, crate::sync::atomic::Ordering::Relaxed);
}
if let Some(handler) = handler {
rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR);
@ -214,7 +214,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
target_os = "fuchsia",
target_os = "horizon",
)))]
static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool =
static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool =
crate::sync::atomic::AtomicBool::new(false);
#[cfg(not(any(
@ -223,8 +223,8 @@ static UNIX_SIGPIPE_ATTR_SPECIFIED: crate::sync::atomic::AtomicBool =
target_os = "fuchsia",
target_os = "horizon",
)))]
pub(crate) fn unix_sigpipe_attr_specified() -> bool {
UNIX_SIGPIPE_ATTR_SPECIFIED.load(crate::sync::atomic::Ordering::Relaxed)
pub(crate) fn on_broken_pipe_flag_used() -> bool {
ON_BROKEN_PIPE_FLAG_USED.load(crate::sync::atomic::Ordering::Relaxed)
}
// SAFETY: must be called only once during runtime cleanup.

View File

@ -353,11 +353,11 @@ impl Command {
// Inherit the signal mask from the parent rather than resetting it (i.e. do not call
// pthread_sigmask).
// If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
// If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
// If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL.
// If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility.
//
// #[unix_sigpipe] is an opportunity to change the default here.
if !crate::sys::pal::unix_sigpipe_attr_specified() {
// -Zon-broken-pipe is an opportunity to change the default here.
if !crate::sys::pal::on_broken_pipe_flag_used() {
#[cfg(target_os = "android")] // see issue #88585
{
let mut action: libc::sigaction = mem::zeroed();
@ -455,7 +455,7 @@ impl Command {
) -> io::Result<Option<Process>> {
use crate::mem::MaybeUninit;
use crate::sys::weak::weak;
use crate::sys::{self, cvt_nz, unix_sigpipe_attr_specified};
use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used};
if self.get_gid().is_some()
|| self.get_uid().is_some()
@ -617,11 +617,11 @@ impl Command {
// Inherit the signal mask from this process rather than resetting it (i.e. do not call
// posix_spawnattr_setsigmask).
// If #[unix_sigpipe] is specified, don't reset SIGPIPE to SIG_DFL.
// If #[unix_sigpipe] is not specified, reset SIGPIPE to SIG_DFL for backward compatibility.
// If -Zon-broken-pipe is used, don't reset SIGPIPE to SIG_DFL.
// If -Zon-broken-pipe is not used, reset SIGPIPE to SIG_DFL for backward compatibility.
//
// #[unix_sigpipe] is an opportunity to change the default here.
if !unix_sigpipe_attr_specified() {
// -Zon-broken-pipe is an opportunity to change the default here.
if !on_broken_pipe_flag_used() {
let mut default_set = MaybeUninit::<libc::sigset_t>::uninit();
cvt(sigemptyset(default_set.as_mut_ptr()))?;
cvt(sigaddset(default_set.as_mut_ptr(), libc::SIGPIPE))?;

View File

@ -1006,6 +1006,13 @@ pub fn rustc_cargo(
cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)");
// If the rustc output is piped to e.g. `head -n1` we want the process to be
// killed, rather than having an error bubble up and cause a panic.
// FIXME: Synthetic #[cfg(bootstrap)]. Remove when the bootstrap compiler supports it.
if compiler.stage != 0 {
cargo.rustflag("-Zon-broken-pipe=kill");
}
// We currently don't support cross-crate LTO in stage0. This also isn't hugely necessary
// and may just be a time sink.
if compiler.stage != 0 {

View File

@ -472,7 +472,7 @@ impl Step for Rustdoc {
features.push("jemalloc".to_string());
}
let cargo = prepare_tool_cargo(
let mut cargo = prepare_tool_cargo(
builder,
build_compiler,
Mode::ToolRustc,
@ -483,6 +483,14 @@ impl Step for Rustdoc {
features.as_slice(),
);
// If the rustdoc output is piped to e.g. `head -n1` we want the process
// to be killed, rather than having an error bubble up and cause a
// panic.
// FIXME: Synthetic #[cfg(bootstrap)]. Remove when the bootstrap compiler supports it.
if build_compiler.stage > 0 {
cargo.rustflag("-Zon-broken-pipe=kill");
}
let _guard = builder.msg_tool(
Kind::Build,
Mode::ToolRustc,

View File

@ -0,0 +1,84 @@
# `on-broken-pipe`
--------------------
The tracking issue for this feature is: [#97889]
Note: The ui for this feature was previously an attribute named `#[unix_sigpipe = "..."]`.
[#97889]: https://github.com/rust-lang/rust/issues/97889
---
## Overview
The `-Zon-broken-pipe=...` compiler flag can be used to specify how libstd shall setup `SIGPIPE` on Unix platforms before invoking `fn main()`. This flag is ignored on non-Unix targets. The flag can be used with three different values or be omitted entirely. It affects `SIGPIPE` before `fn main()` and before children get `exec()`'ed:
| Compiler flag | `SIGPIPE` before `fn main()` | `SIGPIPE` before child `exec()` |
|----------------------------|------------------------------|---------------------------------|
| not used | `SIG_IGN` | `SIG_DFL` |
| `-Zon-broken-pipe=kill` | `SIG_DFL` | not touched |
| `-Zon-broken-pipe=error` | `SIG_IGN` | not touched |
| `-Zon-broken-pipe=inherit` | not touched | not touched |
## `-Zon-broken-pipe` not used
If `-Zon-broken-pipe` is not used, libstd will behave in the manner it has since 2014, before Rust 1.0. `SIGPIPE` will be set to `SIG_IGN` before `fn main()` and result in `EPIPE` errors which are converted to `std::io::ErrorKind::BrokenPipe`.
When spawning child processes, `SIGPIPE` will be set to `SIG_DFL` before doing the underlying `exec()` syscall.
## `-Zon-broken-pipe=kill`
Set the `SIGPIPE` handler to `SIG_DFL` before invoking `fn main()`. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output.
When spawning child processes, `SIGPIPE` will not be touched. This normally means child processes inherit `SIG_DFL` for `SIGPIPE`.
### Example
```rust,no_run
fn main() {
loop {
println!("hello world");
}
}
```
```console
$ rustc -Zon-broken-pipe=kill main.rs
$ ./main | head -n1
hello world
```
## `-Zon-broken-pipe=error`
Set the `SIGPIPE` handler to `SIG_IGN` before invoking `fn main()`. This will result in `ErrorKind::BrokenPipe` errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers.
When spawning child processes, `SIGPIPE` will not be touched. This normally means child processes inherit `SIG_IGN` for `SIGPIPE`.
### Example
```rust,no_run
fn main() {
loop {
println!("hello world");
}
}
```
```console
$ rustc -Zon-broken-pipe=error main.rs
$ ./main | head -n1
hello world
thread 'main' panicked at library/std/src/io/stdio.rs:1118:9:
failed printing to stdout: Broken pipe (os error 32)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
## `-Zon-broken-pipe=inherit`
Leave `SIGPIPE` untouched before entering `fn main()`. Unless the parent process has changed the default `SIGPIPE` handler from `SIG_DFL` to something else, this will behave the same as `-Zon-broken-pipe=kill`.
When spawning child processes, `SIGPIPE` will not be touched. This normally means child processes inherit `SIG_DFL` for `SIGPIPE`.

View File

@ -1,62 +0,0 @@
# `unix_sigpipe`
The tracking issue for this feature is: [#97889]
[#97889]: https://github.com/rust-lang/rust/issues/97889
---
The `#[unix_sigpipe = "..."]` attribute on `fn main()` can be used to specify how libstd shall setup `SIGPIPE` on Unix platforms before invoking `fn main()`. This attribute is ignored on non-Unix targets. There are three variants:
* `#[unix_sigpipe = "inherit"]`
* `#[unix_sigpipe = "sig_dfl"]`
* `#[unix_sigpipe = "sig_ign"]`
## `#[unix_sigpipe = "inherit"]`
Leave `SIGPIPE` untouched before entering `fn main()`. Unless the parent process has changed the default `SIGPIPE` handler from `SIG_DFL` to something else, this will behave the same as `#[unix_sigpipe = "sig_dfl"]`.
## `#[unix_sigpipe = "sig_dfl"]`
Set the `SIGPIPE` handler to `SIG_DFL`. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output.
### Example
```rust,no_run
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"]
fn main() { loop { println!("hello world"); } }
```
```bash
% ./main | head -n 1
hello world
```
## `#[unix_sigpipe = "sig_ign"]`
Set the `SIGPIPE` handler to `SIG_IGN` before invoking `fn main()`. This will result in `ErrorKind::BrokenPipe` errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers.
This is what libstd has done by default since 2014. (However, see the note on child processes below.)
### Example
```rust,no_run
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_ign"]
fn main() { loop { println!("hello world"); } }
```
```bash
% ./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 backtrace
```
### Note on child processes
When spawning child processes, the legacy Rust behavior if `#[unix_sigpipe]` is not specified is to
reset `SIGPIPE` to `SIG_DFL`.
If `#[unix_sigpipe = "..."]` is specified, no matter what its value is, the signal disposition of
`SIGPIPE` is no longer reset. This means that the child inherits the parent's `SIGPIPE` behavior.

View File

@ -387,7 +387,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
let main_ptr = ecx.fn_ptr(FnVal::Instance(entry_instance));
// Always using DEFAULT is okay since we don't support signals in Miri anyway.
// (This means we are effectively ignoring `#[unix_sigpipe]`.)
// (This means we are effectively ignoring `-Zon-broken-pipe`.)
let sigpipe = rustc_session::config::sigpipe::DEFAULT;
ecx.call_function(

View File

@ -207,7 +207,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
),
// Entry point:
gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
ungated!(start, Normal, template!(Word), WarnFollowing),
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),

View File

@ -1,6 +1,3 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"]
fn main() {
rustdoc::main()
}

View File

@ -1,4 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe] //~ error: malformed `unix_sigpipe` attribute input
fn main() {}

View File

@ -1,8 +0,0 @@
error: malformed `unix_sigpipe` attribute input
--> $DIR/unix_sigpipe-bare.rs:3:1
|
LL | #[unix_sigpipe]
| ^^^^^^^^^^^^^^^ help: must be of the form: `#[unix_sigpipe = "inherit|sig_ign|sig_dfl"]`
error: aborting due to 1 previous error

View File

@ -1,4 +0,0 @@
#![feature(unix_sigpipe)]
#![unix_sigpipe = "sig_dfl"] //~ error: `unix_sigpipe` attribute cannot be used at crate level
fn main() {}

View File

@ -1,17 +0,0 @@
error: `unix_sigpipe` attribute cannot be used at crate level
--> $DIR/unix_sigpipe-crate.rs:2:1
|
LL | #![unix_sigpipe = "sig_dfl"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL |
LL | fn main() {}
| ---- the inner attribute doesn't annotate this function
|
help: perhaps you meant to use an outer attribute
|
LL - #![unix_sigpipe = "sig_dfl"]
LL + #[unix_sigpipe = "sig_dfl"]
|
error: aborting due to 1 previous error

View File

@ -1,5 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_ign"]
#[unix_sigpipe = "inherit"] //~ error: multiple `unix_sigpipe` attributes
fn main() {}

View File

@ -1,14 +0,0 @@
error: multiple `unix_sigpipe` attributes
--> $DIR/unix_sigpipe-different-duplicates.rs:4:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unix_sigpipe-different-duplicates.rs:3:1
|
LL | #[unix_sigpipe = "sig_ign"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,5 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"]
#[unix_sigpipe = "inherit"] //~ error: multiple `unix_sigpipe` attributes
fn main() {}

View File

@ -1,14 +0,0 @@
error: multiple `unix_sigpipe` attributes
--> $DIR/unix_sigpipe-duplicates.rs:4:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
note: attribute also specified here
--> $DIR/unix_sigpipe-duplicates.rs:3:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,4 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe(sig_dfl)] //~ error: malformed `unix_sigpipe` attribute input
fn main() {}

View File

@ -1,8 +0,0 @@
error: malformed `unix_sigpipe` attribute input
--> $DIR/unix_sigpipe-ident-list.rs:3:1
|
LL | #[unix_sigpipe(sig_dfl)]
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[unix_sigpipe = "inherit|sig_ign|sig_dfl"]`
error: aborting due to 1 previous error

View File

@ -1,6 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
fn f() {}
fn main() {}

View File

@ -1,8 +0,0 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-non-main-fn.rs:3:1
|
LL | #[unix_sigpipe = "sig_dfl"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,8 +0,0 @@
#![feature(unix_sigpipe)]
mod m {
#[unix_sigpipe = "sig_dfl"] //~ error: `unix_sigpipe` attribute can only be used on root `fn main()`
fn main() {}
}
fn main() {}

View File

@ -1,8 +0,0 @@
error: `unix_sigpipe` attribute can only be used on root `fn main()`
--> $DIR/unix_sigpipe-non-root-main.rs:4:5
|
LL | #[unix_sigpipe = "sig_dfl"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,6 +0,0 @@
#![feature(start)]
#![feature(unix_sigpipe)]
#[start]
#[unix_sigpipe = "sig_dfl"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
fn custom_start(argc: isize, argv: *const *const u8) -> isize { 0 }

View File

@ -1,8 +0,0 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-start.rs:5:1
|
LL | #[unix_sigpipe = "sig_dfl"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,4 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe("sig_dfl")] //~ error: malformed `unix_sigpipe` attribute input
fn main() {}

View File

@ -1,8 +0,0 @@
error: malformed `unix_sigpipe` attribute input
--> $DIR/unix_sigpipe-str-list.rs:3:1
|
LL | #[unix_sigpipe("sig_dfl")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[unix_sigpipe = "inherit|sig_ign|sig_dfl"]`
error: aborting due to 1 previous error

View File

@ -1,6 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
struct S;
fn main() {}

View File

@ -1,8 +0,0 @@
error: `unix_sigpipe` attribute can only be used on `fn main()`
--> $DIR/unix_sigpipe-struct.rs:3:1
|
LL | #[unix_sigpipe = "sig_dfl"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,4 +0,0 @@
#![feature(unix_sigpipe)]
#[unix_sigpipe = "wrong"] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
fn main() {}

View File

@ -1,8 +0,0 @@
error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
--> $DIR/unix_sigpipe-wrong.rs:3:1
|
LL | #[unix_sigpipe = "wrong"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error

View File

@ -1,4 +0,0 @@
#![crate_type = "bin"]
#[unix_sigpipe = "inherit"] //~ the `#[unix_sigpipe]` attribute is an experimental feature
fn main () {}

View File

@ -1,13 +0,0 @@
error[E0658]: the `#[unix_sigpipe]` attribute is an experimental feature
--> $DIR/feature-gate-unix_sigpipe.rs:3:1
|
LL | #[unix_sigpipe = "inherit"]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #97889 <https://github.com/rust-lang/rust/issues/97889> for more information
= help: add `#![feature(unix_sigpipe)]` to the crate attributes to enable
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -6,16 +6,14 @@
//@ ignore-horizon
//@ ignore-android
//@ normalize-stderr-test ".rs:\d+:\d+" -> ".rs:LL:CC"
//@ compile-flags: -Zon-broken-pipe=error
// Test what the error message looks like when `println!()` panics because of
// `std::io::ErrorKind::BrokenPipe`
#![feature(unix_sigpipe)]
use std::env;
use std::process::{Command, Stdio};
#[unix_sigpipe = "sig_ign"]
fn main() {
let mut args = env::args();
let me = args.next().unwrap();

View File

@ -1,8 +1,6 @@
//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
//@ compile-flags: -Zon-broken-pipe=inherit
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"]
fn main() {
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View File

@ -1,8 +1,6 @@
//@ aux-crate: sigpipe_utils=sigpipe-utils.rs
//@ compile-flags: -Zon-broken-pipe=inherit
#![feature(unix_sigpipe)]
#[unix_sigpipe = "inherit"]
fn main() {
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View File

@ -1,25 +1,20 @@
//@ revisions: default sig_dfl sig_ign inherit
//@ revisions: default error kill inherit
//@ ignore-cross-compile because aux-bin does not yet support it
//@ only-unix because SIGPIPE is a unix thing
//@ run-pass
//@ aux-bin:assert-sigpipe-disposition.rs
//@ aux-crate:sigpipe_utils=sigpipe-utils.rs
//@ [kill] compile-flags: -Zunstable-options -Zon-broken-pipe=kill
//@ [error] compile-flags: -Zunstable-options -Zon-broken-pipe=error
//@ [inherit] compile-flags: -Zunstable-options -Zon-broken-pipe=inherit
// Checks the signal disposition of `SIGPIPE` in child processes, and in our own
// process for robustness. Without any `unix_sigpipe` attribute, `SIG_IGN` is
// the default. But there is a difference in how `SIGPIPE` is treated in child
// processes with and without the attribute. Search for
// `unix_sigpipe_attr_specified()` in the code base to learn more.
#![cfg_attr(any(sig_dfl, sig_ign, inherit), feature(unix_sigpipe))]
// process for robustness.
extern crate sigpipe_utils;
use sigpipe_utils::*;
#[cfg_attr(sig_dfl, unix_sigpipe = "sig_dfl")]
#[cfg_attr(sig_ign, unix_sigpipe = "sig_ign")]
#[cfg_attr(inherit, unix_sigpipe = "inherit")]
fn main() {
// By default we get SIG_IGN but the child gets SIG_DFL through an explicit
// reset before exec:
@ -27,18 +22,18 @@ fn main() {
#[cfg(default)]
let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_DFL");
// With #[unix_sigpipe = "sig_dfl"] we get SIG_DFL and the child does too
// without any special code running before exec.
#[cfg(sig_dfl)]
// We get SIG_DFL and the child does too without any special code running
// before exec.
#[cfg(kill)]
let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");
// With #[unix_sigpipe = "sig_ign"] we get SIG_IGN and the child does too
// without any special code running before exec.
#[cfg(sig_ign)]
// We get SIG_IGN and the child does too without any special code running
// before exec.
#[cfg(error)]
let (we_expect, child_expects) = (SignalHandler::Ignore, "SIG_IGN");
// With #[unix_sigpipe = "inherit"] we get SIG_DFL and the child does too
// without any special code running before exec.
// We get SIG_DFL and the child does too without any special code running
// before exec.
#[cfg(inherit)]
let (we_expect, child_expects) = (SignalHandler::Default, "SIG_DFL");

View File

@ -0,0 +1,4 @@
//@ compile-flags: -Zon-broken-pipe=default
//@ check-fail
fn main() {}

View File

@ -0,0 +1,2 @@
error: incorrect value `default` for unstable option `on-broken-pipe` - either `kill`, `error`, or `inherit` was expected

View File

@ -1,12 +1,10 @@
//@ revisions: with_feature without_feature
//@ run-pass
//@ aux-build:sigpipe-utils.rs
#![cfg_attr(with_feature, feature(unix_sigpipe))]
//@ compile-flags: -Zon-broken-pipe=error
fn main() {
extern crate sigpipe_utils;
// SIGPIPE shall be ignored since #[unix_sigpipe = "..."] is not used
// `-Zon-broken-pipe=error` is active, so we expect SIGPIPE to be ignored.
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View File

@ -3,20 +3,20 @@
//@ aux-bin: assert-inherit-sig_dfl.rs
//@ aux-bin: assert-inherit-sig_ign.rs
//@ run-pass
//@ compile-flags: -Zon-broken-pipe=kill
#![feature(rustc_private, unix_sigpipe)]
#![feature(rustc_private)]
extern crate libc;
// By default the Rust runtime resets SIGPIPE to SIG_DFL before exec'ing child
// processes so opt-out of that with `#[unix_sigpipe = "sig_dfl"]`. See
// processes so opt-out of that with `-Zon-broken-pipe=kill`. See
// https://github.com/rust-lang/rust/blob/bf4de3a874753bbee3323081c8b0c133444fed2d/library/std/src/sys/pal/unix/process/process_unix.rs#L359-L384
#[unix_sigpipe = "sig_dfl"]
fn main() {
// First expect SIG_DFL in a child process with #[unix_sigpipe = "inherit"].
// First expect SIG_DFL in a child process with -`Zon-broken-pipe=inherit`.
assert_inherit_sigpipe_disposition("auxiliary/bin/assert-inherit-sig_dfl");
// With SIG_IGN we expect #[unix_sigpipe = "inherit"] to also get SIG_IGN.
// With SIG_IGN we expect `-Zon-broken-pipe=inherit` to also get SIG_IGN.
unsafe {
libc::signal(libc::SIGPIPE, libc::SIG_IGN);
}

View File

@ -1,13 +1,11 @@
//@ run-pass
//@ aux-build:sigpipe-utils.rs
//@ compile-flags: -Zon-broken-pipe=kill
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_dfl"]
fn main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE shall NOT be ignored, instead
// `-Zon-broken-pipe=kill` is active, so SIGPIPE shall NOT be ignored, instead
// the default handler shall be installed
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View File

@ -0,0 +1,4 @@
//@ compile-flags: -Zon-broken-pipe
//@ check-fail
fn main() {}

View File

@ -0,0 +1,2 @@
error: unstable option `on-broken-pipe` requires either `kill`, `error`, or `inherit` (Z on-broken-pipe=<value>)

View File

@ -1,13 +1,9 @@
//@ run-pass
//@ aux-build:sigpipe-utils.rs
#![feature(unix_sigpipe)]
#[unix_sigpipe = "sig_ign"]
fn main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_ign"] is active, so the legacy behavior of ignoring
// SIGPIPE shall be in effect
// SIGPIPE shall be ignored since `-Zon-broken-pipe` is not used
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
}

View File

@ -1,15 +1,14 @@
//@ run-pass
//@ aux-build:sigpipe-utils.rs
//@ compile-flags: -Zon-broken-pipe=kill
#![feature(unix_sigpipe)]
#![feature(rustc_attrs)]
#[unix_sigpipe = "sig_dfl"]
#[rustc_main]
fn rustc_main() {
extern crate sigpipe_utils;
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE handler shall be
// `-Zon-broken-pipe=kill` is active, so SIGPIPE handler shall be
// SIG_DFL. Note that we have a #[rustc_main], but it should still work.
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
}

View File

@ -0,0 +1,4 @@
//@ compile-flags: -Zon-broken-pipe=wrong
//@ check-fail
fn main() {}

View File

@ -0,0 +1,2 @@
error: incorrect value `wrong` for unstable option `on-broken-pipe` - either `kill`, `error`, or `inherit` was expected