Browse Source

Add <daemons> element to <parallel> to start <daemon> threads which

continue to run after the <parallel> task has completed.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@274950 13f79535-47bb-0310-9956-ffa450edef68
master
Conor MacNeill 22 years ago
parent
commit
db75fcb34e
3 changed files with 143 additions and 54 deletions
  1. +10
    -2
      WHATSNEW
  2. +19
    -0
      docs/manual/CoreTasks/parallel.html
  3. +114
    -52
      src/main/org/apache/tools/ant/taskdefs/Parallel.java

+ 10
- 2
WHATSNEW View File

@@ -499,7 +499,7 @@ Other changes:

* OpenVMS is detected as a valid OS family.

* DirectoryScanner has been optimized for cases where include patterns do not
* DirectoryScanner has been optimized for cases where include patterns do not
start with wildcards. Bugzilla Report 20103.

* Added keep-going feature. Bugzilla Report 21144
@@ -514,7 +514,15 @@ Other changes:
an enablemultiplemapping attribute. Bugzilla Report 21320.

* <exec> will now work on OpenVMS (please read the notes in
<exec>'s manual page). Bugzilla Report 21877.
<exec>'s manual page). Bugzilla Report 21877.

* <parallel> now supports a timeout which can be used to recover
from deadlocks, etc in the parallel threads. <parallel> also
now supports a <daemons> nested element. This can be used to
run tasks in daemon threads which the parallel task will not
wait for before completing. A new attribute failonany will cause
<parallel> to throw an exception if any thread fails without
waiting for all other threads to complete.

Changes from Ant 1.5.2 to Ant 1.5.3
===================================


+ 19
- 0
docs/manual/CoreTasks/parallel.html View File

@@ -99,6 +99,25 @@ taking up all available threads before the tasks that would unlock the waitfor
would occur. This is not a repalcement for Java Language level thread
semantics and is best used for "embarassingly parallel" tasks.</p>


<h3>Parameters specified as nested elements</h3>

<h4>daemons</h4>
<p>
The parallel task supports a &lt;daemons&gt; 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.
</p>

<p>Daemon tasks can be used, for example, to start test servers which might not be easily
terminated from Ant. By using &lt;daemons&gt; such servers do not halt the build.
</p>


<h3>Examples</h3>
<pre>
&lt;parallel&gt;


+ 114
- 52
src/main/org/apache/tools/ant/taskdefs/Parallel.java View File

@@ -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).
* <p>
* @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;
}

/**


Loading…
Cancel
Save