Author: sgoeschl Date: Sun Oct 9 10:40:55 2011 New Revision: 1180579 URL: http://svn.apache.org/viewvc?rev=1180579&view=rev Log: [EXEX-60] - Fixed dead lock by calling the timeout observers outside of the synchronized block
Modified: commons/proper/exec/trunk/src/changes/changes.xml commons/proper/exec/trunk/src/main/java/org/apache/commons/exec/Watchdog.java commons/proper/exec/trunk/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java Modified: commons/proper/exec/trunk/src/changes/changes.xml URL: http://svn.apache.org/viewvc/commons/proper/exec/trunk/src/changes/changes.xml?rev=1180579&r1=1180578&r2=1180579&view=diff ============================================================================== --- commons/proper/exec/trunk/src/changes/changes.xml (original) +++ commons/proper/exec/trunk/src/changes/changes.xml Sun Oct 9 10:40:55 2011 @@ -24,6 +24,11 @@ </properties> <body> <release version="1.1.1-SNAPSHOT" date="TBA" description="Bugfixing Release"> + <action issue="EXEC-60" dev="sgoeschl" type="fix" date="2011-10-09" due-to="Peter Kofler"> + Fixed dead lock by calling the timeout observers outside of the synchronized block thereby + removing on pre-requisite of a deadlock. Also added a test case to demonstrate that this + problem is fixed (which of course can not guarantee the absence of a dead lock). + </action> <action issue="EXEC-55" dev="sgoeschl" type="add" date="2011-02-21" due-to="Dominik Stadler"> Set names for started threads. </action> Modified: commons/proper/exec/trunk/src/main/java/org/apache/commons/exec/Watchdog.java URL: http://svn.apache.org/viewvc/commons/proper/exec/trunk/src/main/java/org/apache/commons/exec/Watchdog.java?rev=1180579&r1=1180578&r2=1180579&view=diff ============================================================================== --- commons/proper/exec/trunk/src/main/java/org/apache/commons/exec/Watchdog.java (original) +++ commons/proper/exec/trunk/src/main/java/org/apache/commons/exec/Watchdog.java Sun Oct 9 10:40:55 2011 @@ -68,17 +68,26 @@ public class Watchdog implements Runnabl notifyAll(); } - public synchronized void run() { - final long until = System.currentTimeMillis() + timeout; - long now; - while (!stopped && until > (now = System.currentTimeMillis())) { - try { - wait(until - now); - } catch (InterruptedException e) { - } - } - if (!stopped) { - fireTimeoutOccured(); - } - } + public void run() { + final long until = System.currentTimeMillis() + timeout; + boolean isWaiting; + synchronized (this) { + long now = System.currentTimeMillis(); + isWaiting = until > now; + while (!stopped && isWaiting) { + try { + wait(until - now); + } catch (InterruptedException e) { + } + now = System.currentTimeMillis(); + isWaiting = until > now; + } + } + + // notify the listeners outside of the synchronized block (see EXEC-60) + if (!isWaiting) { + fireTimeoutOccured(); + } + } + } Modified: commons/proper/exec/trunk/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java URL: http://svn.apache.org/viewvc/commons/proper/exec/trunk/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java?rev=1180579&r1=1180578&r2=1180579&view=diff ============================================================================== --- commons/proper/exec/trunk/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java (original) +++ commons/proper/exec/trunk/src/test/java/org/apache/commons/exec/DefaultExecutorTest.java Sun Oct 9 10:40:55 2011 @@ -956,10 +956,10 @@ public class DefaultExecutorTest extends // start an asynchronous process to enable the main thread System.out.println("Preparing to execute process - commandLine=" + cl.toString()); - DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler(); + DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler(); exec.execute(cl, handler); System.out.println("Process spun off successfully - process=" + cl.getExecutable()); - + int x; PipedInputStream pis = new PipedInputStream(pipedOutputStream); while ((x = pis.read()) >= 0) { @@ -1010,8 +1010,46 @@ public class DefaultExecutorTest extends } pis.close(); - handler.waitFor(); + handler.waitFor(); + } + } + + /** + * Test EXEC-60 (https://issues.apache.org/jira/browse/EXEC-60). + * + * Possible deadlock when a process is terminating at the same time its timing out. Please + * note that a successful test is no proof that the issues was indeed fixed. + * + * @throws Exception the test failed + */ + public void testExec_60() throws IOException { + + int start = 0; + final int seconds = 1; + int processTerminatedCounter = 0; + int watchdogKilledProcessCounter = 0; + CommandLine cmdLine = new CommandLine(pingScript); + cmdLine.addArgument(Integer.toString(seconds + 1)); // need to add "1" to wait the requested number of seconds + + for (int offset = start; offset <= 20; offset += 1) { + ExecuteWatchdog watchdog = new ExecuteWatchdog(seconds * 1000 + offset); + exec.setWatchdog(watchdog); + try { + exec.execute(cmdLine); + processTerminatedCounter++; + System.out.println(offset + ": process has terminated: " + watchdog.killedProcess()); + if(processTerminatedCounter > 5) { + break; + } + } catch (ExecuteException ex) { + System.out.println(offset + ": process was killed: " + watchdog.killedProcess()); + assertTrue("Watchdog killed the process", watchdog.killedProcess()); + watchdogKilledProcessCounter++;; + } } + + assertTrue("Not a single process terminated on its own", processTerminatedCounter > 0); + assertTrue("Not a single process was killed by the watch dog", watchdogKilledProcessCounter > 0); } // ====================================================================== @@ -1062,7 +1100,7 @@ public class DefaultExecutorTest extends String text; StringBuffer contents = new StringBuffer(); - BufferedReader reader = new BufferedReader(new FileReader(file)); + BufferedReader reader = new BufferedReader(new FileReader(file)); while ((text = reader.readLine()) != null) {