JDK-4850368 : (process) getInputStream() attaches to forked background processes (Linux)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang
  • Affected Version: 1.4.1_02,1.4.2
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2003-04-17
  • Updated: 2003-04-22
  • Resolved: 2003-04-22
Related Reports
Duplicate :  

Name: nt126004			Date: 04/17/2003

java version "1.4.1_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_01-b01)
Java HotSpot(TM) Client VM (build 1.4.1_01-b01, mixed mode)


java version "1.4.2-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-rc-b20)
Java HotSpot(TM) Client VM (build 1.4.2-rc-b20, mixed mode)

Linux 7ctadev01 2.4.7-10 #1 Thu Sep 6 17:27:27 EDT 2001 i686 unknown

When using Runtime.exec() to run a shell script that spawns background processes, reads against the InputStream that is returned by Process.getInputStream() will block until the spawned process dies, even if that process is launched in the background and it's output is redirected.

[root@7ctadev01 tmp]# cat test.sh
echo "Launching sleep script now"
sleep 10 1>/dev/null 2>&1 &
echo "Done launching sleep script now"

[root@7ctadev01 tmp]# /usr/java/jdk1.3.1_02/bin/java Exec ./test.sh
stdout : Launching sleep script now
stdout : Done launching sleep script now
Done reading standard output
It took 35 milliseconds to read output

[root@7ctadev01 tmp]# /usr/java/j2sdk1.4.1_01/bin/java Exec ./test.sh
stdout : Launching sleep script now
stdout : Done launching sleep script now
Done reading standard output
It took 10043 milliseconds to read output

The run with jdk 1.4.1 should have completed before the process spawned by the test.sh script ended, as the run with jdk 1.3.1 did.  The timing was added to the code in an effort to prove that the Exec program was trying to read the output from the spawned process.

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;

public class Exec {

    public static void main(String[] args) {

        String command = "";
        for (int i = 0; i < args.length; i++) {
            command += args[i];

            if (i != args.length) command += " ";

        try {
            long startTime = System.currentTimeMillis();

            Process process = Runtime.getRuntime().exec(command);

            BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line = null;
            while ((line = br.readLine()) != null) {
                System.out.println("stdout : " + line);

            System.out.println("Done reading standard output");

            System.out.println("It took " + (System.currentTimeMillis() - startTime)
                + " milliseconds to read output");

        } catch(Exception e) {

---------- END SOURCE ----------
(Review ID: 183327) 

PUBLIC COMMENTS Reads from the input stream of an exec'ed process do not always get an EOF at the appropriate time because not all file descriptors are correctly closed. Will be fixed when 4843136 is fixed.

EVALUATION I have reproduced this on Linux, but not on Solaris. On Linux, I have reproduced it with the current development versions of 1.5 and 1.4.2, as well as 1.4.1_02. This appears to be the old "file descriptors not being closed in the child" bug, which has resisted fixing efforts, but appears to finally be fixed as a result of the pending fix to 4843136 (process) pipe file descriptor from Runtime.exec not being closed I have verified that my not-yet-integrated patch for this bug in fact causes the reported problem to go away. The reported test case is very interesting because it is good for reproducing this bug on Linux. Test cases I have developed independently seem to be more reproducible on multi-CPU solaris than on uni-CPU Linux. Here's what seems to be happening: After the fork, the child calls (UNIXProcess_md.c.linux) dup2(fdout[1], 1); to create the stdout for the about-to-be-execed process. Unfortunately, fdout[1] is not explicitly closed, and the bug in closeDescriptors (4843136) prevents that descriptor from being closed since the parent closes that descriptor immediately. The child in turn execs another process with stdout redirected, but the grandchild inherits that fdout[1] descriptor, preventing the grandparent from getting EOF on process.getInputStream(). This particular test case doesn't show up on Solaris since UNIXProcess_md.c.solaris explicitly closes fdout[1]. These kinds of subtle differences between Solaris and Linux should be eliminated eventually when 4790606 (process) Native forkAndExec code should be unified (sol, lnx) is addressed. ###@###.### 2003-04-22