Why does the pipe cause the panic in the standard library?

In this simple example, the program has a probability that there is a panic in the standard library.

The call side is

use std::{io::{Read, Write}, process::Stdio};

fn main() {
    //call(show);
    let mut o = std::process::Command::new("sh")
        .arg("-c")
		.arg("./echo")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();
	let into = o.stdin.take();
	let mut w =into.unwrap();
	for i in 0..10{
		w.write_all(format!("hello {i}\n").as_bytes()).unwrap();
	}
	let mut c = [0u8;256];
	let out =  o.stdout.take();
	let size = out.unwrap().read(& mut c).unwrap();
	loop{  // wait
		std::thread::sleep(std::time::Duration::from_secs(1));
	}
}

And the echo side is

use std::io::Read;

fn main() {
    let mut o = std::io::stdin();
    loop {
		let mut buf = String::new();
        match o.read_line(& mut buf) {
            Ok(size) => {
                if size == 0 {
                    break;
                }
                println!("receive {}", buf);
            }
            Err(e) => {}
        }
    }
}

The call side writes some data to echo through the pipeline, the echo reads these data from the pipeline and appends the receive in the received data.

However, this example can cause panic thrown from the standard library.

thread 'main' panicked at library/std/src/io/stdio.rs:1021:9:
failed printing to stdout: Broken pipe (os error 32)

Sometimes the program can successfully run.

Why is the pipe broken? What's the issue here?

A broken pipe when writing means that the read side of the pipe closed the pipe. Can you show a backtrace? You can set the RUST_BACKTRACE env var to 1 to make rust print a backtrace when panicking.

The backtrace is

0: rust_begin_unwind
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
   1: core::panicking::panic_fmt
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
   2: core::result::unwrap_failed
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1649:5
   3: core::result::Result<T,E>::unwrap
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1073:23
   4: echo::main
             at ./src/bin/echo.rs:23:3
   5: core::ops::function::FnOnce::call_once
             at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

I found that the pipe would't be broken if the echo side didn't write \n, that is, buf.trim(). I don't know why.

The full backtrace is:

  0:        0x1082257f5 - std::backtrace_rs::backtrace::libunwind::trace::he87ba3c236c7ad5f
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/../../backtrace/src/backtrace/libunwind.rs:104:5
   1:        0x1082257f5 - std::backtrace_rs::backtrace::trace_unsynchronized::h3ad5d899409e49ee
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:        0x1082257f5 - std::sys_common::backtrace::_print_fmt::hb1551f966d2dd86d
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:68:5
   3:        0x1082257f5 - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hbd71adb7a72f4105
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:44:22
   4:        0x10823d353 - core::fmt::rt::Argument::fmt::h4224d647cce844bf
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/fmt/rt.rs:142:9
   5:        0x10823d353 - core::fmt::write::h30346430340bc336
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/fmt/mod.rs:1120:17
   6:        0x10822394e - std::io::Write::write_fmt::heb3d6316c565d5b1
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/io/mod.rs:1810:15
   7:        0x1082255c9 - std::sys_common::backtrace::_print::hc99e5bf521524ac2
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:47:5
   8:        0x1082255c9 - std::sys_common::backtrace::print::h67e51ff2e3d5cfbd
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:34:9
   9:        0x108226a25 - std::panicking::default_hook::{{closure}}::hc666c9a55318d1f1
  10:        0x10822679e - std::panicking::default_hook::hf980b1da49948523
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:292:9
  11:        0x108226ea3 - std::panicking::rust_panic_with_hook::h683bce980186bbbe
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:779:13
  12:        0x108226db4 - std::panicking::begin_panic_handler::{{closure}}::ha6dbd11ba0ec8af1
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:657:13
  13:        0x108225ce9 - std::sys_common::backtrace::__rust_end_short_backtrace::h889430bddc786c98
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:171:18
  14:        0x108226af2 - rust_begin_unwind
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:645:5
  15:        0x108243a25 - core::panicking::panic_fmt::hff768cef35397791
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/panicking.rs:72:14
  16:        0x108243e85 - core::result::unwrap_failed::h951d84d71b0bada2
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1649:5
  17:        0x108205017 - core::result::Result<T,E>::unwrap::h65e410238800e7da
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/result.rs:1073:23
  18:        0x108206391 - echo::main::ha0d28540aac9100f
                               at /Users/xieminghao/Documents/rust-workspace/hello/src/bin/echo.rs:23:3
  19:        0x1082051ae - core::ops::function::FnOnce::call_once::h54d859177681efe7
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:250:5
  20:        0x1082066e1 - std::sys_common::backtrace::__rust_begin_short_backtrace::h8b4da017eb6a7346
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/sys_common/backtrace.rs:155:18
  21:        0x108207134 - std::rt::lang_start::{{closure}}::h6936a187ccba52d0
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/rt.rs:166:18
  22:        0x1082213d0 - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::h28f55c80744bada5
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/core/src/ops/function.rs:284:13
  23:        0x1082213d0 - std::panicking::try::do_call::h2a7711271ca00d3c
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:552:40
  24:        0x1082213d0 - std::panicking::try::h8cfa57ed24255592
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:516:19
  25:        0x1082213d0 - std::panic::catch_unwind::h4ba1cfc637be3672
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panic.rs:142:14
  26:        0x1082213d0 - std::rt::lang_start_internal::{{closure}}::he9dd7d8444a0863e
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/rt.rs:148:48
  27:        0x1082213d0 - std::panicking::try::do_call::h4f92b6155a985b32
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:552:40
  28:        0x1082213d0 - std::panicking::try::h1810f6567656aa0f
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panicking.rs:516:19
  29:        0x1082213d0 - std::panic::catch_unwind::h979a0e4ea0734504
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/panic.rs:142:14
  30:        0x1082213d0 - std::rt::lang_start_internal::hba631f1493ca29ca
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/rt.rs:148:20
  31:        0x108207107 - std::rt::lang_start::h2b76de54448fc189
                               at /rustc/07dca489ac2d933c78d3c5158e3f43beefeb02ce/library/std/src/rt.rs:165:17
  32:        0x108206408 - _main

Seems there are three ways to mitigate the broken pipe.

  1. Change buf to buf.trim() in the echo side
  2. change o.read_line to o.read in the echo side
  3. change the echo side to the following:
    let mut o = std::io::stdin();
	let mut out = std::io::stdout();
	let mut buf = [0u8;1024];
    loop {
        match o.read(& mut buf) {
            Ok(size) => {
                if size == 0 {
                    continue;
                }
				let r = format!("receive {}\n", String::from_utf8_lossy(&buf[..size]));
				out.write_all(r.as_bytes()).unwrap();
				out.flush();
            }
            Err(e) => {
				panic!("{e:?}");
				//continue;
			}
        };
    }

The latter can have more probability of executing successfully than the former. The third is almost 99% successful in my test. I don't know what caused the pipe to break

It's expected. See this issue and/or the many linked issues. I think there's crates to ignore or abort on pipe errors if you want to go that way, or probably inline workarounds in the issues.

If you want to deal with it yourself without touching the signal handlers, you need to use writeln! (etc) instead of println!.

2 Likes