What is the reason for the JVM handling SIGPIPE the way it does?
I would've expected for
java foo | head -10
with
public class Foo {
public static void main(String[] args){
Stream.iterate(0, n -> n + 1).forEach(System.out::println);
}
}
to cause the process to be killed when writing the 11th line, however that is not the case. Instead, it seems that only a trouble
flag is being set at the PrintStream, which can be checked through System.out.checkError()
.
What happens is that the SIGPIPE exception results in an IOException.
For most OutputStream
and Writer
classes, this exception propagates through the "write" method, and has to be handled by the caller.
However, when you are writing to System.out, you are using a PrintStream
, and that class by design takes care of the IOException
of you. As the javadoc says:
A
PrintStream
adds functionality to another output stream, namely the ability to print representations of various data values conveniently. Two other features are provided as well. Unlike other output streams, aPrintStream
never throws anIOException
; instead, exceptional situations merely set an internal flag that can be tested via thecheckError
method.
What is the reason for the JVM handling SIGPIPE the way it does?
The above explains what is happening. The "why" is ... I guess ... that the designers wanted to make PrintStream
easy to use for typical use cases of System.out
where the caller doesn't want to deal with a possible IOException
on every call.
Unfortunately, there is no elegant solution to this:
checkError
...FileDescriptor.out
object, and wrap it in a new FileOutputStream
object ... and use that instead of System.out
.Note that there are no strong guarantees that the Java app will only write 10 lines of output in java foo | head -1
. It is quite possible for the app to write-ahead many lines, and to only "see" the pipe closed after head
has gotten around to reading the first 10 of them. This applies with System.out
(and checkError
) or if you wrap FileDescriptor
.