From db75fcb34ed8d469fe7ff1a0e7afddf4a1d89177 Mon Sep 17 00:00:00 2001
From: Conor MacNeill
+The parallel task supports a <daemons> nested element. This is a list of tasks +which are to be run in parallel daemon threads. The parallel task will not wait for +these tasks to complete. Being daemon threads, however, they will not prevent Ant from +completing, whereupon the threads are terminated. Failures in daemon threads which +occur before the parallel task itself finishes will be reported and can cause +parallel to throw an exception. Failures which occur after parallel has completed are not +reported. +
+ +Daemon tasks can be used, for example, to start test servers which might not be easily +terminated from Ant. By using <daemons> such servers do not halt the build. +
+ +
<parallel>
diff --git a/src/main/org/apache/tools/ant/taskdefs/Parallel.java b/src/main/org/apache/tools/ant/taskdefs/Parallel.java
index cc4b86d0d..cd3d8dc22 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Parallel.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Parallel.java
@@ -56,6 +56,8 @@ package org.apache.tools.ant.taskdefs;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Vector;
+import java.util.List;
+import java.util.ArrayList;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Task;
@@ -86,6 +88,21 @@ import org.apache.tools.ant.util.StringUtils;
public class Parallel extends Task
implements TaskContainer {
+ /** Class which holds a list of tasks to execute */
+ public static class TaskList implements TaskContainer {
+ /** Collection holding the nested tasks */
+ private List tasks = new ArrayList();
+
+ /**
+ * Add a nested task to execute parallel (asynchron).
+ *
+ * @param nestedTask Nested task to be executed in parallel
+ */
+ public void addTask(Task nestedTask) throws BuildException {
+ tasks.add(nestedTask);
+ }
+ }
+
/** Collection holding the nested tasks */
private Vector nestedTasks = new Vector();
@@ -98,12 +115,13 @@ public class Parallel extends Task
/** Total number of threads per processor to run. */
private int numThreadsPerProcessor = 0;
+ /** The timeout period in milliseconds */
private long timeout;
/** Indicates threads are still running and new threads can be issued */
private volatile boolean stillRunning;
- /** INdicates that the execution timedout */
+ /** Indicates that the execution timedout */
private boolean timedOut;
/**
@@ -112,6 +130,31 @@ public class Parallel extends Task
*/
private boolean failOnAny;
+ /** The dameon task list if any */
+ private TaskList daemonTasks;
+
+ /** Accumulation of exceptions messages from all nested tasks */
+ private StringBuffer exceptionMessage;
+
+ /** Number of exceptions from nested tasks */
+ private int numExceptions = 0;
+
+ /** The first exception encountered */
+ private Throwable firstException;
+
+ /** The location of the first exception */
+ private Location firstLocation;
+
+ /**
+ * Add a group of daemon threads
+ */
+ public void addDaemons(TaskList daemonTasks) {
+ if (this.daemonTasks != null) {
+ throw new BuildException("Only one daemon group is supported");
+ }
+ this.daemonTasks = daemonTasks;
+ }
+
/**
* Interval to poll for completed threads when threadCount or
* threadsPerProcessor is specified. Integer in milliseconds.; optional
@@ -211,6 +254,27 @@ public class Parallel extends Task
}
}
+ private void processExceptions(TaskRunnable[] runnables) {
+ if (runnables == null) {
+ return;
+ }
+ for (int i = 0; i < runnables.length; ++i) {
+ Throwable t = runnables[i].getException();
+ if (t != null) {
+ numExceptions++;
+ if (firstException == null) {
+ firstException = t;
+ }
+ if (t instanceof BuildException
+ && firstLocation == Location.UNKNOWN_LOCATION) {
+ firstLocation = ((BuildException) t).getLocation();
+ }
+ exceptionMessage.append(StringUtils.LINE_SEP);
+ exceptionMessage.append(t.getMessage());
+ }
+ }
+ }
+
/**
* Spin up required threads with a maximum number active at any given time.
*
@@ -227,7 +291,7 @@ public class Parallel extends Task
threadNumber++) {
Task nestedTask = (Task) e.nextElement();
runnables[threadNumber]
- = new TaskRunnable(threadNumber, nestedTask);
+ = new TaskRunnable(nestedTask);
}
final int maxRunning = numTasks < numThreads ? numTasks : numThreads;
@@ -236,37 +300,52 @@ public class Parallel extends Task
threadNumber = 0;
ThreadGroup group = new ThreadGroup("parallel");
- // now run them in limited numbers...
- // start initial batch of threads
- for (int i = 0; i < maxRunning; ++i) {
- running[i] = runnables[threadNumber++];
- Thread thread = new Thread(group, running[i]);
- thread.start();
+ TaskRunnable[] daemons = null;
+ if (daemonTasks != null && daemonTasks.tasks.size() != 0) {
+ daemons = new TaskRunnable[daemonTasks.tasks.size()];
}
- if (timeout != 0) {
- // start the timeout thread
- Thread timeoutThread = new Thread() {
- public synchronized void run() {
- try {
- wait(timeout);
- synchronized (semaphore) {
- stillRunning = false;
- timedOut = true;
- semaphore.notifyAll();
+ synchronized (semaphore) {
+ // start any daemon threads
+ if (daemons != null) {
+ for (int i = 0; i < daemons.length; ++i) {
+ daemons[i] = new TaskRunnable((Task) daemonTasks.tasks.get(i));
+ Thread daemonThread = new Thread(group, daemons[i]);
+ daemonThread.setDaemon(true);
+ daemonThread.start();
+ }
+ }
+
+ // now run main threads in limited numbers...
+ // start initial batch of threads
+ for (int i = 0; i < maxRunning; ++i) {
+ running[i] = runnables[threadNumber++];
+ Thread thread = new Thread(group, running[i]);
+ thread.start();
+ }
+
+ if (timeout != 0) {
+ // start the timeout thread
+ Thread timeoutThread = new Thread() {
+ public synchronized void run() {
+ try {
+ wait(timeout);
+ synchronized (semaphore) {
+ stillRunning = false;
+ timedOut = true;
+ semaphore.notifyAll();
+ }
+ } catch (InterruptedException e) {
+ // ignore
}
- } catch (InterruptedException e) {
- // ignore
}
- }
- };
- timeoutThread.start();
- }
+ };
+ timeoutThread.start();
+ }
- // now find available running slots for the remaining threads
- outer:
- while (threadNumber < numTasks && stillRunning) {
- synchronized (semaphore) {
+ // now find available running slots for the remaining threads
+ outer:
+ while (threadNumber < numTasks && stillRunning) {
for (int i = 0; i < maxRunning; i++) {
if (running[i] == null || running[i].finished) {
running[i] = runnables[threadNumber++];
@@ -288,9 +367,7 @@ public class Parallel extends Task
// sheesh!
}
}
- }
- synchronized (semaphore) {
// are all threads finished
outer2:
while (stillRunning) {
@@ -315,25 +392,12 @@ public class Parallel extends Task
}
// now did any of the threads throw an exception
- StringBuffer exceptionMessage = new StringBuffer();
- int numExceptions = 0;
- Throwable firstException = null;
- Location firstLocation = Location.UNKNOWN_LOCATION;
- for (int i = 0; i < numTasks; ++i) {
- Throwable t = runnables[i].getException();
- if (t != null) {
- numExceptions++;
- if (firstException == null) {
- firstException = t;
- }
- if (t instanceof BuildException
- && firstLocation == Location.UNKNOWN_LOCATION) {
- firstLocation = ((BuildException) t).getLocation();
- }
- exceptionMessage.append(StringUtils.LINE_SEP);
- exceptionMessage.append(t.getMessage());
- }
- }
+ exceptionMessage = new StringBuffer();
+ numExceptions = 0;
+ firstException = null;
+ firstLocation = Location.UNKNOWN_LOCATION;
+ processExceptions(daemons);
+ processExceptions(runnables);
if (numExceptions == 1) {
if (firstException instanceof BuildException) {
@@ -373,7 +437,6 @@ public class Parallel extends Task
private class TaskRunnable implements Runnable {
private Throwable exception;
private Task task;
- private int taskNumber;
boolean finished;
/**
@@ -381,9 +444,8 @@ public class Parallel extends Task
*
* @param task the Task to be executed in a seperate thread
*/
- TaskRunnable(int taskNumber, Task task) {
+ TaskRunnable(Task task) {
this.task = task;
- this.taskNumber = taskNumber;
}
/**