1. stuff that is shared read is always marked volatile, to avoid being compiled out. 2. added more synchronization when appropriate. I make no claims as to thread safety here, as I was never that good at formal proofs of correctness. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@465013 13f79535-47bb-0310-9956-ffa450edef68master
@@ -12,6 +12,10 @@ Other changes: | |||||
* Upgraded XML API and parser to Xerces 2.8.1 | * Upgraded XML API and parser to Xerces 2.8.1 | ||||
* A code review of some threaded logic has tightened up the synchronization | |||||
of Watchdog, ExecuteWatchdog and ExecuteJava, which could reduce the occurence | |||||
of race conditions here, especially on Java1.5+. | |||||
Changes from Ant 1.7.0Beta2 to Ant 1.7.0Beta3 | Changes from Ant 1.7.0Beta2 to Ant 1.7.0Beta3 | ||||
============================================= | ============================================= | ||||
@@ -24,6 +28,13 @@ Changes that could break older environments: | |||||
the java class file. Bugzilla report 33604. | the java class file. Bugzilla report 33604. | ||||
* Defer reference process. Bugzilla 36955, 34458, 37688. | * Defer reference process. Bugzilla 36955, 34458, 37688. | ||||
This may break build files in which a reference was set in a target which was | |||||
never executed. Historically, Ant would set the reference early on, during parse | |||||
time, so the datatype would be defined. Now it requires the reference to have | |||||
been in a bit of the build file which was actually executed. If you get | |||||
an error about an undefined reference, locate the reference and move it somewhere | |||||
where it is used, or fix the depends attribute of the target in question to | |||||
depend on the target which defines the reference/datatype. | |||||
Fixed bugs: | Fixed bugs: | ||||
----------- | ----------- | ||||
@@ -50,8 +50,8 @@ public class ExecuteJava implements Runnable, TimeoutObserver { | |||||
private Permissions perm = null; | private Permissions perm = null; | ||||
private Method main = null; | private Method main = null; | ||||
private Long timeout = null; | private Long timeout = null; | ||||
private Throwable caught = null; | |||||
private boolean timedOut = false; | |||||
private volatile Throwable caught = null; | |||||
private volatile boolean timedOut = false; | |||||
private Thread thread = null; | private Thread thread = null; | ||||
/** | /** | ||||
@@ -45,13 +45,13 @@ public class ExecuteWatchdog implements TimeoutObserver { | |||||
private Process process; | private Process process; | ||||
/** say whether or not the watchdog is currently monitoring a process */ | /** say whether or not the watchdog is currently monitoring a process */ | ||||
private boolean watch = false; | |||||
private volatile boolean watch = false; | |||||
/** exception that might be thrown during the process execution */ | /** exception that might be thrown during the process execution */ | ||||
private Exception caught = null; | private Exception caught = null; | ||||
/** say whether or not the process was killed due to running overtime */ | /** say whether or not the process was killed due to running overtime */ | ||||
private boolean killedProcess = false; | |||||
private volatile boolean killedProcess = false; | |||||
/** will tell us whether timeout has occurred */ | /** will tell us whether timeout has occurred */ | ||||
private Watchdog watchdog; | private Watchdog watchdog; | ||||
@@ -103,15 +103,15 @@ public class ExecuteWatchdog implements TimeoutObserver { | |||||
*/ | */ | ||||
public synchronized void stop() { | public synchronized void stop() { | ||||
watchdog.stop(); | watchdog.stop(); | ||||
watch = false; | |||||
process = null; | |||||
cleanUp(); | |||||
} | } | ||||
/** | /** | ||||
* Called after watchdog has finished. | * Called after watchdog has finished. | ||||
* This can be called in the watchdog thread | |||||
* @param w the watchdog | * @param w the watchdog | ||||
*/ | */ | ||||
public void timeoutOccured(Watchdog w) { | |||||
public synchronized void timeoutOccured(Watchdog w) { | |||||
try { | try { | ||||
try { | try { | ||||
// We must check if the process was not stopped | // We must check if the process was not stopped | ||||
@@ -135,7 +135,7 @@ public class ExecuteWatchdog implements TimeoutObserver { | |||||
/** | /** | ||||
* reset the monitor flag and the process. | * reset the monitor flag and the process. | ||||
*/ | */ | ||||
protected void cleanUp() { | |||||
protected synchronized void cleanUp() { | |||||
watch = false; | watch = false; | ||||
process = null; | process = null; | ||||
} | } | ||||
@@ -148,7 +148,7 @@ public class ExecuteWatchdog implements TimeoutObserver { | |||||
* @throws BuildException a wrapped exception over the one that was | * @throws BuildException a wrapped exception over the one that was | ||||
* silently swallowed and stored during the process run. | * silently swallowed and stored during the process run. | ||||
*/ | */ | ||||
public void checkException() throws BuildException { | |||||
public synchronized void checkException() throws BuildException { | |||||
if (caught != null) { | if (caught != null) { | ||||
throw new BuildException("Exception in ExecuteWatchdog.run: " | throw new BuildException("Exception in ExecuteWatchdog.run: " | ||||
+ caught.getMessage(), caught); | + caught.getMessage(), caught); | ||||
@@ -33,7 +33,16 @@ public class Watchdog implements Runnable { | |||||
private Vector observers = new Vector(1); | private Vector observers = new Vector(1); | ||||
private long timeout = -1; | private long timeout = -1; | ||||
private boolean stopped = false; | |||||
/** | |||||
* marked as volatile to stop the compiler caching values or (in java1.5+, | |||||
* reordering access) | |||||
*/ | |||||
private volatile boolean stopped = false; | |||||
/** | |||||
* Error string. | |||||
* {@value} | |||||
*/ | |||||
public static final String ERROR_INVALID_TIMEOUT = "timeout less than 1."; | |||||
/** | /** | ||||
* Constructor for Watchdog. | * Constructor for Watchdog. | ||||
@@ -41,7 +50,7 @@ public class Watchdog implements Runnable { | |||||
*/ | */ | ||||
public Watchdog(long timeout) { | public Watchdog(long timeout) { | ||||
if (timeout < 1) { | if (timeout < 1) { | ||||
throw new IllegalArgumentException("timeout less than 1."); | |||||
throw new IllegalArgumentException(ERROR_INVALID_TIMEOUT); | |||||
} | } | ||||
this.timeout = timeout; | this.timeout = timeout; | ||||
} | } | ||||
@@ -51,6 +60,7 @@ public class Watchdog implements Runnable { | |||||
* @param to the timeout observer to add. | * @param to the timeout observer to add. | ||||
*/ | */ | ||||
public void addTimeoutObserver(TimeoutObserver to) { | public void addTimeoutObserver(TimeoutObserver to) { | ||||
//no need to synchronize, as Vector is always synchronized | |||||
observers.addElement(to); | observers.addElement(to); | ||||
} | } | ||||
@@ -59,11 +69,13 @@ public class Watchdog implements Runnable { | |||||
* @param to the timeout observer to remove. | * @param to the timeout observer to remove. | ||||
*/ | */ | ||||
public void removeTimeoutObserver(TimeoutObserver to) { | public void removeTimeoutObserver(TimeoutObserver to) { | ||||
//no need to synchronize, as Vector is always synchronized | |||||
observers.removeElement(to); | observers.removeElement(to); | ||||
} | } | ||||
/** | /** | ||||
* Inform the observers that a timeout has occurred. | * Inform the observers that a timeout has occurred. | ||||
* This happens in the watchdog thread. | |||||
*/ | */ | ||||
protected final void fireTimeoutOccured() { | protected final void fireTimeoutOccured() { | ||||
Enumeration e = observers.elements(); | Enumeration e = observers.elements(); | ||||