You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Parallel.java 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. * Copyright (c) 2001-2003 The Apache Software Foundation. All rights
  5. * reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The end-user documentation included with the redistribution, if
  20. * any, must include the following acknowlegement:
  21. * "This product includes software developed by the
  22. * Apache Software Foundation (http://www.apache.org/)."
  23. * Alternately, this acknowlegement may appear in the software itself,
  24. * if and wherever such third-party acknowlegements normally appear.
  25. *
  26. * 4. The names "Ant" and "Apache Software
  27. * Foundation" must not be used to endorse or promote products derived
  28. * from this software without prior written permission. For written
  29. * permission, please contact apache@apache.org.
  30. *
  31. * 5. Products derived from this software may not be called "Apache"
  32. * nor may "Apache" appear in their names without prior written
  33. * permission of the Apache Group.
  34. *
  35. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  36. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  37. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  38. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  39. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  41. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  42. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  43. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  44. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  45. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  46. * SUCH DAMAGE.
  47. * ====================================================================
  48. *
  49. * This software consists of voluntary contributions made by many
  50. * individuals on behalf of the Apache Software Foundation. For more
  51. * information on the Apache Software Foundation, please see
  52. * <http://www.apache.org/>.
  53. */
  54. package org.apache.tools.ant.taskdefs;
  55. import java.lang.reflect.Method;
  56. import java.util.Enumeration;
  57. import java.util.Vector;
  58. import org.apache.tools.ant.BuildException;
  59. import org.apache.tools.ant.Location;
  60. import org.apache.tools.ant.Task;
  61. import org.apache.tools.ant.TaskContainer;
  62. import org.apache.tools.ant.util.StringUtils;
  63. /**
  64. * Executes the contained tasks in separate threads, continuing
  65. * once all are completed.<br/>
  66. * New behavior allows for the ant script to specify a maximum number of
  67. * threads that will be executed in parallel. One should be very careful about
  68. * using the <code>waitFor</code> task when specifying <code>threadCount</code>
  69. * as it can cause deadlocks if the number of threads is too small or if one of
  70. * the nested tasks fails to execute completely. The task selection algorithm
  71. * will insure that the tasks listed before a task have started before that
  72. * task is started, but it will not insure a successful completion of those
  73. * tasks or that those tasks will finish first (i.e. it's a classic race
  74. * condition).
  75. * <p>
  76. * @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a>
  77. * @author Conor MacNeill
  78. * @author Danno Ferrin
  79. * @since Ant 1.4
  80. *
  81. * @ant.task category="control"
  82. */
  83. public class Parallel extends Task
  84. implements TaskContainer {
  85. /** Collection holding the nested tasks */
  86. private Vector nestedTasks = new Vector();
  87. /** Semaphore to notify of completed threads */
  88. private final Object semaphore = new Object();
  89. /** Total number of threads to run */
  90. int numThreads = 0;
  91. /** Total number of threads per processor to run. */
  92. int numThreadsPerProcessor = 0;
  93. /** Interval (in ms) to poll for finished threads. */
  94. int pollInterval = 1000; // default is once a second
  95. /**
  96. * Add a nested task to execute in parallel.
  97. * @param nestedTask Nested task to be executed in parallel
  98. */
  99. public void addTask(Task nestedTask) throws BuildException {
  100. nestedTasks.addElement(nestedTask);
  101. }
  102. /**
  103. * Dynamically generates the number of threads to execute based on the
  104. * number of available processors (via
  105. * <code>java.lang.Runtime.availableProcessors()</code>). Requires a J2SE
  106. * 1.4 VM, and it will overwrite the value set in threadCount.
  107. * If used in a 1.1, 1.2, or 1.3 VM then the task will defer to
  108. * <code>threadCount</code>.; optional
  109. * @param numThreadsPerProcessor Number of threads to create per available
  110. * processor.
  111. *
  112. */
  113. public void setThreadsPerProcessor(int numThreadsPerProcessor) {
  114. this.numThreadsPerProcessor = numThreadsPerProcessor;
  115. }
  116. /**
  117. * Statically determine the maximum number of tasks to execute
  118. * simultaneously. If there are less tasks than threads then all will be
  119. * executed at once, if there are more then only <code>threadCount</code>
  120. * tasks will be executed at one time. If <code>threadsPerProcessor</code>
  121. * is set and the JVM is at least a 1.4 VM then this value is ignormed.; optional
  122. *
  123. * @param numThreads total number of therads.
  124. *
  125. */
  126. public void setThreadCount(int numThreads) {
  127. this.numThreads = numThreads;
  128. }
  129. /**
  130. * Interval to poll for completed threads when threadCount or
  131. * threadsPerProcessor is specified. Integer in milliseconds.; optional
  132. *
  133. * @param pollInterval New value of property pollInterval.
  134. */
  135. public void setPollInterval(int pollInterval) {
  136. this.pollInterval = pollInterval;
  137. }
  138. public void execute() throws BuildException {
  139. updateThreadCounts();
  140. if (numThreads == 0) {
  141. spinAllThreads();
  142. } else {
  143. spinNumThreads();
  144. }
  145. }
  146. public void updateThreadCounts() {
  147. if (numThreadsPerProcessor != 0) {
  148. int numProcessors = getNumProcessors();
  149. if (numProcessors != 0) {
  150. numThreads = numProcessors * numThreadsPerProcessor;
  151. }
  152. }
  153. }
  154. /**
  155. * Spin up threadCount threads.
  156. */
  157. public void spinNumThreads() throws BuildException {
  158. final int maxThreads = nestedTasks.size();
  159. Thread[] threads = new Thread[maxThreads];
  160. TaskThread[] taskThreads = new TaskThread[maxThreads];
  161. int threadNumber = 0;
  162. for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();
  163. threadNumber++) {
  164. Task nestedTask = (Task) e.nextElement();
  165. ThreadGroup group = new ThreadGroup("parallel");
  166. TaskThread taskThread = new TaskThread(threadNumber, nestedTask);
  167. taskThreads[threadNumber] = taskThread;
  168. threads[threadNumber] = new Thread(group, taskThread);
  169. }
  170. final int maxRunning = numThreads;
  171. Thread[] running = new Thread[maxRunning];
  172. threadNumber = 0;
  173. // now run them in limited numbers...
  174. outer:
  175. while (threadNumber < maxThreads) {
  176. synchronized(semaphore) {
  177. for (int i = 0; i < maxRunning; i++) {
  178. if (running[i] == null || !running[i].isAlive()) {
  179. running[i] = threads[threadNumber++];
  180. running[i].start();
  181. // countinue on outer while loop in case we used our last thread
  182. continue outer;
  183. }
  184. }
  185. // if we got here all are running, so sleep a little
  186. try {
  187. semaphore.wait(pollInterval);
  188. } catch (InterruptedException ie) {
  189. // dosen't java know interruptions are rude?
  190. // just pretend it didn't happen and go aobut out business.
  191. // sheesh!
  192. }
  193. }
  194. }
  195. // now join to all the threads
  196. for (int i = 0; i < maxRunning; ++i) {
  197. try {
  198. if (running[i] != null) {
  199. running[i].join();
  200. }
  201. } catch (InterruptedException ie) {
  202. // who would interrupt me at a time like this?
  203. }
  204. }
  205. // now did any of the threads throw an exception
  206. StringBuffer exceptionMessage = new StringBuffer();
  207. int numExceptions = 0;
  208. Throwable firstException = null;
  209. Location firstLocation = Location.UNKNOWN_LOCATION;;
  210. for (int i = 0; i < maxThreads; ++i) {
  211. Throwable t = taskThreads[i].getException();
  212. if (t != null) {
  213. numExceptions++;
  214. if (firstException == null) {
  215. firstException = t;
  216. }
  217. if (t instanceof BuildException &&
  218. firstLocation == Location.UNKNOWN_LOCATION) {
  219. firstLocation = ((BuildException) t).getLocation();
  220. }
  221. exceptionMessage.append(StringUtils.LINE_SEP);
  222. exceptionMessage.append(t.getMessage());
  223. }
  224. }
  225. if (numExceptions == 1) {
  226. if (firstException instanceof BuildException) {
  227. throw (BuildException) firstException;
  228. } else {
  229. throw new BuildException(firstException);
  230. }
  231. } else if (numExceptions > 1) {
  232. throw new BuildException(exceptionMessage.toString(),
  233. firstLocation);
  234. }
  235. }
  236. /**
  237. * Spin up one thread per task.
  238. */
  239. public void spinAllThreads() throws BuildException {
  240. int numTasks = nestedTasks.size();
  241. Thread[] threads = new Thread[numTasks];
  242. TaskThread[] taskThreads = new TaskThread[numTasks];
  243. int threadNumber = 0;
  244. for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();
  245. threadNumber++) {
  246. Task nestedTask = (Task) e.nextElement();
  247. ThreadGroup group = new ThreadGroup("parallel");
  248. TaskThread taskThread = new TaskThread(threadNumber, nestedTask);
  249. taskThreads[threadNumber] = taskThread;
  250. threads[threadNumber] = new Thread(group, taskThread);
  251. }
  252. // now start all threads
  253. for (int i = 0; i < threads.length; ++i) {
  254. threads[i].start();
  255. }
  256. // now join to all the threads
  257. for (int i = 0; i < threads.length; ++i) {
  258. try {
  259. threads[i].join();
  260. } catch (InterruptedException ie) {
  261. // who would interrupt me at a time like this?
  262. }
  263. }
  264. // now did any of the threads throw an exception
  265. StringBuffer exceptionMessage = new StringBuffer();
  266. int numExceptions = 0;
  267. Throwable firstException = null;
  268. Location firstLocation = Location.UNKNOWN_LOCATION;;
  269. for (int i = 0; i < threads.length; ++i) {
  270. Throwable t = taskThreads[i].getException();
  271. if (t != null) {
  272. numExceptions++;
  273. if (firstException == null) {
  274. firstException = t;
  275. }
  276. if (t instanceof BuildException &&
  277. firstLocation == Location.UNKNOWN_LOCATION) {
  278. firstLocation = ((BuildException) t).getLocation();
  279. }
  280. exceptionMessage.append(StringUtils.LINE_SEP);
  281. exceptionMessage.append(t.getMessage());
  282. }
  283. }
  284. if (numExceptions == 1) {
  285. if (firstException instanceof BuildException) {
  286. throw (BuildException) firstException;
  287. } else {
  288. throw new BuildException(firstException);
  289. }
  290. } else if (numExceptions > 1) {
  291. throw new BuildException(exceptionMessage.toString(),
  292. firstLocation);
  293. }
  294. }
  295. public int getNumProcessors() {
  296. try {
  297. Class[] paramTypes = {};
  298. Method availableProcessors =
  299. Runtime.class.getMethod("availableProcessors", paramTypes);
  300. Object[] args = {};
  301. Integer ret = (Integer) availableProcessors.invoke(Runtime.getRuntime(), args);
  302. return ret.intValue();
  303. } catch (Exception e) {
  304. // return a bogus number
  305. return 0;
  306. }
  307. }
  308. /**
  309. * thread that execs a task
  310. */
  311. private class TaskThread implements Runnable {
  312. private Throwable exception;
  313. private Task task;
  314. private int taskNumber;
  315. /**
  316. * Construct a new TaskThread.<p>
  317. *
  318. * @param task the Task to be executed in a seperate thread
  319. */
  320. TaskThread(int taskNumber, Task task) {
  321. this.task = task;
  322. this.taskNumber = taskNumber;
  323. }
  324. /**
  325. * Executes the task within a thread and takes care about
  326. * Exceptions raised within the task.
  327. */
  328. public void run() {
  329. try {
  330. task.perform();
  331. } catch (Throwable t) {
  332. exception = t;
  333. } finally {
  334. synchronized (semaphore) {
  335. semaphore.notifyAll();
  336. }
  337. }
  338. }
  339. /**
  340. * get any exception that got thrown during execution;
  341. * @return an exception or null for no exception/not yet finished
  342. */
  343. public Throwable getException() {
  344. return exception;
  345. }
  346. }
  347. }