Author: cutting Date: Fri Jan 6 13:35:35 2006 New Revision: 366571 URL: http://svn.apache.org/viewcvs?rev=366571&view=rev Log: Fix for NUTCH-151: CommandRunner can hang after the main thread exec is finished and has inefficient busy loop.
I encountered a case where the JVM of a Tasktracker child did not exit after the main thread returned; a thread dump showed only the threads named STDOUT and STDERR from CommandRunner as non-daemon threads, and both were doing a read(). CommandRunner also had an excessively costly busy loop. These problems were fixed by: 1. The pipe io threads should be daemons. 2. The main thread should always interrupt() the pipe io threads when finishing up, not just when a timeout occurs. 3. Sleep before testing whether the process has finished with Process.exitValue(). 4. Increased the sleep time to be 1000msec. By Paul Baclace. Modified: lucene/nutch/trunk/src/java/org/apache/nutch/util/CommandRunner.java Modified: lucene/nutch/trunk/src/java/org/apache/nutch/util/CommandRunner.java URL: http://svn.apache.org/viewcvs/lucene/nutch/trunk/src/java/org/apache/nutch/util/CommandRunner.java?rev=366571&r1=366570&r2=366571&view=diff ============================================================================== --- lucene/nutch/trunk/src/java/org/apache/nutch/util/CommandRunner.java (original) +++ lucene/nutch/trunk/src/java/org/apache/nutch/util/CommandRunner.java Fri Jan 6 13:35:35 2006 @@ -18,6 +18,7 @@ * Adopted by John Xing for Nutch Project from * http://blog.fivesight.com/prb/space/Call+an+External+Command+from+Java/, * which explains the code in detail. + * [Original author is moving his site to http://mult.ifario.us/ -peb] * * Comments by John Xing on 20040621: * (1) EDU.oswego.cs.dl.util.concurrent.* is in j2sdk 1.5 now. @@ -31,6 +32,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.InterruptedIOException; import EDU.oswego.cs.dl.util.concurrent.BrokenBarrierException; import EDU.oswego.cs.dl.util.concurrent.CyclicBarrier; @@ -80,40 +82,47 @@ } public void evaluate() throws IOException { - Process proc = Runtime.getRuntime().exec(_command); + this.exec(); + } + /** + * + * @return process exit value (return code) or -1 if timed out. + * @throws IOException + */ + public int exec() throws IOException { + Process proc = Runtime.getRuntime().exec(_command); _barrier = new CyclicBarrier(3 + ((_stdin != null) ? 1 : 0)); PullerThread so = new PullerThread("STDOUT", proc.getInputStream(), _stdout); + so.setDaemon(true); so.start(); PullerThread se = new PullerThread("STDERR", proc.getErrorStream(), _stderr); + se.setDaemon(true); se.start(); PusherThread si = null; if (_stdin != null) { si = new PusherThread("STDIN", _stdin, proc.getOutputStream()); + si.setDaemon(true); si.start(); } boolean _timedout = false; long end = System.currentTimeMillis() + _timeout * 1000; + // try { if (_timeout == 0) { - _barrier.barrier(); + _barrier.barrier(); // JDK 1.5: // _barrier.await(); } else { - _barrier.attemptBarrier(_timeout * 1000); + _barrier.attemptBarrier(_timeout * 1000); // JDK 1.5: // _barrier.await(_timeout, TimeUnit.SECONDS); } } catch (TimeoutException ex) { _timedout = true; - if (si != null) { - si.interrupt(); - } - so.interrupt(); - se.interrupt(); if (_destroyOnTimeout) { proc.destroy(); } @@ -123,16 +132,27 @@ /* IGNORE */ } + // tell the io threads we are finished + if (si != null) { + si.interrupt(); + } + so.interrupt(); + se.interrupt(); + _xit = -1; if (!_timedout) { if (_waitForExit) { do { try { + Thread.sleep(1000); _xit = proc.exitValue(); - Thread.sleep(250); } catch (InterruptedException ie) { - /* IGNORE */ + if (Thread.interrupted()) { + break; // stop waiting on an interrupt for this thread + } else { + continue; + } } catch (IllegalThreadStateException iltse) { continue; } @@ -152,6 +172,7 @@ proc.destroy(); } } + return _xit; } public Throwable getThrownError() { @@ -163,8 +184,6 @@ private OutputStream _os; private InputStream _is; - private volatile boolean _kaput; - private boolean _closeInput; protected PumperThread( @@ -179,7 +198,6 @@ } public void run() { - _kaput = false; try { byte[] buf = new byte[BUF]; int read = 0; @@ -189,9 +207,10 @@ _os.write(buf, 0, read); _os.flush(); } + } catch (InterruptedIOException iioe) { + // ignored } catch (Throwable t) { _thrownError = t; - return; } finally { try { if (_closeInput) { @@ -203,13 +222,6 @@ /* IGNORE */ } } - try { - _barrier.barrier(); - } catch (InterruptedException ie) { - /* IGNORE */ - } catch (BrokenBarrierException bbe) { - /* IGNORE */ - } } } @@ -254,7 +266,7 @@ String filePath = null; int timeout = 10; - String usage = "Usage: CommandRunner [-timeout timeout] commandPath filePath"; + String usage = "Usage: CommandRunner [-timeout timeoutSecs] commandPath filePath"; if (args.length < 2) { System.err.println(usage);