United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4763384 : (process) Intermittent hang on piped input (lnx)

Details
Type:
Bug
Submit Date:
2002-10-15
Status:
Resolved
Updated Date:
2002-12-17
Project Name:
JDK
Resolved Date:
2002-12-17
Component:
core-libs
OS:
linux
Sub-Component:
java.lang
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
1.4.1
Fixed Versions:
1.4.2 (b11)

Related Reports
Relates:
Relates:

Sub Tasks

Description

Name: rmT116609			Date: 10/15/2002


FULL PRODUCT VERSION :
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)


FULL OPERATING SYSTEM VERSION :
Linux kernel 2.2.14-6.1.1, also seen on 2.2.16 glibc-2.1.3-26



A DESCRIPTION OF THE PROBLEM :
When using Runtime.exec in java1.4.1 on an external program
that needs to read standard input, the program will hang on
a pipe read about 50% of the time.  Under 1.4, this never
occurred.  Since the external program never consumes its
input, it never exits or performs its work and thus
Process.waitFor will hang.

REGRESSION.  Last worked in version 1.4

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. compile the attached test program (client JVM)
2. run repeatedly under java1.4.1
3. should hang within a few runs and hang about 50% time
overall.

EXPECTED VERSUS ACTUAL BEHAVIOR :
When the program is working correctly (java 1.4,
java1.3.1), it will print the exit code and then exit.
When it is failing it will hang with no exit code printed.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error message.   Program hangs.

REPRODUCIBILITY :
This bug can be reproduced often.

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

/**
 * This class demonstrates a regression in java1.4.1 in the handling of
 * the Process OutputStream (exec'd process stdin).   main executes
 * to completion 100% of the time in java1.4, but about only about 50%
 * of the time under 1.4.1.  Issue exists for client JVM, Linux Redhat 6.2
 * not sure about other variants of Linux or other Os, or server JVM.
 */
public class ExecRegression
{
    private static final String CAT = "/bin/cat";

    public static void main(String[] args)
    {
      /*
       * Execute /bin/cat supplying two lines of input.  cat should
       * read the input lines and copy them to stdout.  On completion,
       * p.waitFor should return and the exit status is printed and this
       * program exits.  Under 1.4.1, cat sometimes gets stuck on a pipe
       * read and never terminates.
       */
      try {
        Process p = Runtime.getRuntime().exec(new String[] { CAT } );
        String input = "This is Line 1\nThis is Line 2\n";
        StringBufferInputStream in = new StringBufferInputStream(input);
        // create threads to handle I/O streams
        IO ioIn = new IO("stdin", in, p.getOutputStream());
        IO ioOut = new IO("stdout", p.getInputStream(), System.out);
        IO ioErr = new IO("stderr", p.getErrorStream(), System.err);

        // wait for process to exit
        int status = p.waitFor();
        System.out.println(status);
        System.exit(status);
     } catch (Exception e) {
        e.printStackTrace();
     }
    }

    private ExecRegression() {}

    /**
     * Handle IO.  Thread is started in constructor.
     */
    static class IO extends Thread {

        private InputStream in;
        private OutputStream out;

        IO(String name, InputStream in, OutputStream out)
        {
            this.in = in;
            this.out = out;
            setName(name);
            start();
        }

        public void run() {
            try {
                int c;
                while ((c = in.read()) != -1) {
                    out.write(c);
                }
                out.flush();
            } catch (IOException e) {
            } finally {
                if (!System.out.equals(out) && !System.err.equals(out)) {
                    // Note: in order to get an exec'd java process to
                    // see EOF on input, it is necessary to close stdin
                    if (out != null)
                        try { out.close(); } catch (Exception e) {}
                }
            }
        }
    }
}


---------- END SOURCE ----------


Release Regression From : 1.4
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Review ID: 165706) 
======================================================================

                                    

Comments
CONVERTED DATA

BugTraq+ Release Management Values

COMMIT TO FIX:
mantis-beta

FIXED IN:
mantis-beta

INTEGRATED IN:
mantis-b11
mantis-beta


                                     
2004-06-14
SUGGESTED FIX

*** /tmp/geta22066	Tue Dec  3 05:07:15 2002
--- UNIXProcess_md.c.linux	Tue Dec  3 05:07:14 2002
***************
*** 375,380 ****
--- 375,389 ----
  	dup2(fderr[1], 2);
  
          /* close everything */
+ 
+ 	/* We can't rely upon the kernel to have updated /proc/pid/fd with the
+ 	 * parent's most-recently-opened descriptors by the time we get here,
+ 	 * so we explicitly close the parent's sides of the in/out/err pipes.
+ 	 */
+ 	close(fdin[1]);
+ 	close(fdout[0]);
+ 	close(fderr[0]);
+ 
          if (closeDescriptors() == 0) { /* failed,  close the old way */
              max_fd = (int)sysconf(_SC_OPEN_MAX);
              for (i = 3; i < max_fd; i++) close(i);
                                     
2004-06-11
EVALUATION

Fairly easy to reproduce using latest 1.4.2 build.  -- ###@###.### 2002/12/2

Another case of /proc/pid/fd not being atomically updated by the kernel.  The
closeDescriptors() procedure in UNIXProcess_md.c.linux doesn't always see all
of the descriptors for the parent's sides of the in/out/err pipes.  This is
easy to work around: We simply to close these three descriptors explicitly
before calling closeDescriptors() to close the rest.

This regression from 1.4.0 should be fixed in 1.4.2 since it affects stability.
The fix is simple and safe.

-- ###@###.### 2002/12/3

There was a deeper underlying bug that was fixed in 

4843136 (process) pipe file descriptor from Runtime.exec not being closed 

###@###.### 2003-04-27
                                     
169-10-12 0
WORK AROUND

Sleep for a very small amount of time (1ms seems to work) immediately after
starting the subprocess, e.g.,

	Process p = Runtime.getRuntime().exec(CAT);
	Thread.sleep(1);
	...

-- ###@###.### 2002/12/2
                                     
168-10-12 0



Hardware and Software, Engineered to Work Together