Search code examples
javamultithreadingprocessoperating-systemreadline

Why does java.lang.process's readline() behave differently for reading inputstream on different boxes with the same os


I tested this code(below) on several different linux boxes(4+) and it worked fine. However, on one linux box I ran into an issue with readline() hanging for the error inputStream(errorStream). This stream should be empty so I suspected that box was not writing out a line terminator to the errorStream for the error. I changed my code to use read() instead of readline()...but read() also hung.

I tried retrieving the input inputStream first, and that worked and there was no hangs with readline()/read() for the error inputstream. I could not do this since I needed to obtain possible errors first. Appearing to be a deadlock, I was able to resolve this by having each inputstream read from it's own thread. Why did I only see this issue on one box? Is there a kernel setting or some other setting specific to this box that could have caused this?

   ProcessBuilder  processBuilder = new ProcessBuilder()
   try 
   {
       Process processA = null;
       synchronized (processBuilder)
       {
           processBuilder.command("/bin/sh","-c"," . /Home/SomeScript.ksh");
           processA = processBuilder.start();
       }

       inputStream = processA.getInputStream();
       reader = new BufferedReader(new InputStreamReader(inputStream));

       errorStream = processA.getErrorStream();
       errorReader = new BufferedReader(new InputStreamReader(errorStream));

       String driverError;

       while ((driverError = errorReader.readLine()) != null)
       {
           //some code
       }

Solution

  • Why did I only see this issue on one box?

    Most likely because of something in the script that is being run ... and its interactions with its environment (e.g. files, environment variables, etc)

    Is there a kernel setting or some other setting specific to this box that could have caused this?

    It is possible but unlikely that it is a kernel setting. It might be "something else". Indeed, it has to be "something" outside of the Java application that is to blame, at least in part.


    I suggest you do the following temporarily (at least):

       ProcessBuilder  processBuilder = new ProcessBuilder();
       processBuilder.command("/bin/sh","-c"," . /Home/SomeScript.ksh");
       processBuilder.redirectErrorStream(true);
    
       processA = processBuilder.start();
    
       inputStream = processA.getInputStream();
       reader = new BufferedReader(new InputStreamReader(inputStream));
       String line;
    
       while ((line = reader.readLine()) != null) {
           System.out.println(line);
       }
       System.out.println("Return code is " + processA.exitValue());
    

    That way you can see what all of the output is.

    There should not be a problem if the external process fails to put a newline at the end of the last line. The Java process will see an EOF on the input stream, and the BufferedReader will return what characters it has ... and return null on the next call.


    Another possibility is that the external process is blocking because it is trying to read from its standard input.


    UPDATE

    The redirectErrorStream also resolved the issue, but I need the error stream separate.

    OK so if it did (reliably) solve the problem then that (most likely) means that you have to read the external processes stdout and stderr streams in parallel. The simple way to do is to create 2 threads to read and buffer the two streams separately. For example: Capturing stdout when calling Runtime.exec

    (Your problem is due to the fact that pipes have a finite buffering capacity. The external problem is most likely alternating between writing stuff to stdout and stderr. If it tries to write to one of the pipes when that pipe is "full", it will block. But if your application is reading all of the other pipe (to EOF) before it reads the blocked pipe, then everything will deadlock. The fact that the external process is stuck in PIPE_W state is more evidence for this explanation.

    One possible reason that you are seeing different behaviour on different systems is that the amount of buffering in a pipe is system dependent. But it could also be due to differences in what the external process is doing; e.g. its inputs.)