Java ProcessBuilder: Resultant Process Hangs
If the process writes to stderr
or stdout
, and you're not reading it - it will just "hang" , blocking when writing to stdout/err
. Either redirect stdout/err
to /dev/null
using a shell or merge stdout/err
with redirectErrorStream(true) and spawn another thread that reads from stdout
of the process
ProcessBuilder hangs
You wait for the program to end before you being reading the piped output, but a pipe only has a limited buffer, so when the buffer is full, the program will wait for you consume the buffered output, but you're waiting on the program to end. Deadlock!
Always consume output before calling waitFor()
.
UPDATE
Recommend you change the code as follows:
val process = ProcessBuilder(command)
.redirectErrorStream(true)
.start()
val stdOut = processResult.inputStream.bufferedReader().readText()
if (process.waitFor(timeoutAmount, timeoutUnit)) {
val exitCode = processResult.exitValue()
return AppResult(exitCode, stdOut, "")
}
// timeout: decide what to do here, since command hasn't terminated yet
There is no need to specify Redirect.PIPE
, since that's the default. If you don't join stderr and stdout like shown here, you'd need to create threads to consume them individually, since they both have the buffer full issue, so you can't just read one of them first.
Why does process hang if the parent does not consume stdout/stderr in Java?
Java doesn't do anything in this area. It just uses OS services to create the pipes.
All Unix like OSs and Windows behave the same in this regard: A pipe with a 4K is created between parent and child. When that pipe is full (because one side isn't reading), the writing process blocks.
This is the standard since the inception of pipes. There is not much Java can do.
What you can argue is that the process API in Java is clumsy and doesn't have good defaults like simply connecting child streams to the same stdin/stdout as the parent unless the developer overrides them with something specific.
I think there are two reasons for the current API. First of all, the Java developers (i.e. the guys at Sun/Oracle) know exactly how the process API works and what you need to do. They know so much that it didn't occur to them that the API could be confusing.
The second reason is that there is no good default that will work for the majority. You can't really connect stdin of the parent and the child; if you type something on the console, to which process should the input go?
Similarly, if you connect stdout, the output will go somewhere. If you have a web app, there might be no console or the output might go somewhere where no one will expect it.
You can't even throw an exception when the pipe is full since that can happen during the normal operation as well.
Related Topics
C++ Equivalent of Java'S Instanceof
How to Round Up the Result of Integer Division
Access Private Field of Another Object in Same Class
How to Access the Java Method in a C++ Application
Is Short Circuit Evaluation Guaranteed in C++ as It Is in Java
How to Efficiently Iterate Over Each Entry in a Java Map
Why Are Only Final Variables Accessible in Anonymous Class
How to Convert a Byte Array to a Hex String in Java
What Is Reflection and Why Is It Useful
In Java, How to Determine the Size of an Object
What Is Pecs (Producer Extends Consumer Super)
How to Upload Files to a Server Using Jsp/Servlet
Servlet Returns "Http Status 404 the Requested Resource (/Servlet) Is Not Available"
How to Fix Java.Lang.Unsupportedclassversionerror: Unsupported Major.Minor Version
How to Generate Random Integers Within a Specific Range in Java
Add a Complex Image in the Panel, With Buttons Around It in One Customized User Interface
How to Test a Class That Has Private Methods, Fields or Inner Classes