Skip to Main Content

Java Programming

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

race condition with Runtime.exec()?

852321Dec 18 2012 — edited Dec 22 2012
Hi all -- I'm having an issue trying to execute a shell command to determine architecture type (uname -m on Linux and Mac). For the record I have read both of the following links thoroughly, and several times:

When Runtime.exec() won't: http://www.javaworld.com/jw-12-2000/jw-1229-traps.html

Five Common java.lang.Process Pitfalls: http://kylecartmell.com/?p=9




The problem seems to be some sort of race condition with the StreamGobblers, which are streams for STDOUT and STDERR. I process the streams and invoke waitFor() and then I package up the return code and the output from the two streams into an Object array. However, it seems like by the time it's returning the Object array, the streams haven't been properly flushed. Just because the initial thread has finished and received a return value doesn't mean that the two gobbler threads have finished flushing their data out...does it?

I'm looking at the first article there and I would assume they need to call join() on the StreamGobblers before invoking p.waitFor(). It seems to resolve the problem for me. Am I wrong about this? If I don't call join() on those threads then I don't see any output in my caller method unless I introduce some sort of delay in the callee, such as stepping through the debugger, or adding in a call to Thread.sleep() right before invoking p.waitFor().

This article is 12 years old so I would expect that someone would have corrected them by now. I can't honestly believe nobody ever noticed this until now, especially with all of the comments there saying that the article saved them. Am I just missing something here? It sure seems like they need to call join() on the gobblers. See "Listing 4.5 GoodWindowsExec.java" on page 4 (http://www.javaworld.com/jw-12-2000/jw-1229-traps.html?page=4) and then compare that to my proposed changes below:
// a bunch of setup stuff above here but not necessary for this example
            Runtime rt = Runtime.getRuntime();
            System.out.println("Execing " + cmd[0] + " " + cmd[1] 
                               + " " + cmd[2]);
            Process proc = rt.exec(cmd);
            // any error message?
            StreamGobbler errorGobbler = new 
                StreamGobbler(proc.getErrorStream(), "ERROR");            
            
            // any output?
            StreamGobbler outputGobbler = new 
                StreamGobbler(proc.getInputStream(), "OUTPUT");
                
            // kick them off
            errorGobbler.start();
            outputGobbler.start();
            errorGobbler.join();      // NEED TO ADD THIS LINE
            outputGobbler.join();      // NEED TO ADD THIS LINE
                                    
            // any error???
            int exitVal = proc.waitFor();
            System.out.println("ExitValue: " + exitVal);

            // I return an object array like this to my caller:
            Object[] ret = new Object[3];
            ret[0] = new Integer(exitVal);
            ret[1] = outputGobbler.getOutputStream();  // I implemented getOutputStream in my version to return a String
            ret[2] = errorGobbler.getOutputStream();   // rather than printing everything with System.out.println()
            return ret;        
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}
Edited by: gamblor01 on Dec 18, 2012 3:42 PM

Edited by: gamblor01 on Dec 18, 2012 3:43 PM

Edited by: gamblor01 on Dec 18, 2012 3:46 PM
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Jan 19 2013
Added on Dec 18 2012
15 comments
821 views