PageRenderTime 62ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/hudson-core/src/main/java/hudson/model/Queue.java

http://github.com/hudson/hudson
Java | 1664 lines | 891 code | 199 blank | 574 comment | 193 complexity | 89d12a0e8d743f859f98a8dcde859878 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause
  1. /*
  2. * The MIT License
  3. *
  4. * Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
  5. * Stephen Connolly, Tom Huybrechts, InfraDNA, Inc.
  6. *
  7. * Permission is hereby granted, free of charge, to any person obtaining a copy
  8. * of this software and associated documentation files (the "Software"), to deal
  9. * in the Software without restriction, including without limitation the rights
  10. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. * copies of the Software, and to permit persons to whom the Software is
  12. * furnished to do so, subject to the following conditions:
  13. *
  14. * The above copyright notice and this permission notice shall be included in
  15. * all copies or substantial portions of the Software.
  16. *
  17. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. * THE SOFTWARE.
  24. */
  25. package hudson.model;
  26. import hudson.AbortException;
  27. import hudson.BulkChange;
  28. import hudson.ExtensionList;
  29. import hudson.ExtensionPoint;
  30. import hudson.Util;
  31. import hudson.XmlFile;
  32. import hudson.init.Initializer;
  33. import static hudson.init.InitMilestone.JOB_LOADED;
  34. import static hudson.util.Iterators.reverse;
  35. import hudson.cli.declarative.CLIMethod;
  36. import hudson.cli.declarative.CLIResolver;
  37. import hudson.model.queue.AbstractQueueTask;
  38. import hudson.model.queue.Executables;
  39. import hudson.model.queue.SubTask;
  40. import hudson.model.queue.FutureImpl;
  41. import hudson.model.queue.MappingWorksheet;
  42. import hudson.model.queue.MappingWorksheet.Mapping;
  43. import hudson.model.queue.QueueSorter;
  44. import hudson.model.queue.QueueTaskDispatcher;
  45. import hudson.model.queue.Tasks;
  46. import hudson.model.queue.WorkUnit;
  47. import hudson.model.Node.Mode;
  48. import hudson.model.listeners.SaveableListener;
  49. import hudson.model.queue.CauseOfBlockage;
  50. import hudson.model.queue.FoldableAction;
  51. import hudson.model.queue.CauseOfBlockage.BecauseLabelIsBusy;
  52. import hudson.model.queue.CauseOfBlockage.BecauseNodeIsOffline;
  53. import hudson.model.queue.CauseOfBlockage.BecauseLabelIsOffline;
  54. import hudson.model.queue.CauseOfBlockage.BecauseNodeIsBusy;
  55. import hudson.model.queue.WorkUnitContext;
  56. import hudson.triggers.SafeTimerTask;
  57. import hudson.triggers.Trigger;
  58. import hudson.util.OneShotEvent;
  59. import hudson.util.TimeUnit2;
  60. import hudson.util.XStream2;
  61. import hudson.util.ConsistentHash;
  62. import hudson.util.ConsistentHash.Hash;
  63. import java.io.BufferedReader;
  64. import java.io.File;
  65. import java.io.FileInputStream;
  66. import java.io.IOException;
  67. import java.io.InputStreamReader;
  68. import java.lang.ref.WeakReference;
  69. import java.util.ArrayList;
  70. import java.util.Arrays;
  71. import java.util.Calendar;
  72. import java.util.Collection;
  73. import java.util.Collections;
  74. import java.util.GregorianCalendar;
  75. import java.util.HashMap;
  76. import java.util.Iterator;
  77. import java.util.List;
  78. import java.util.Map;
  79. import java.util.NoSuchElementException;
  80. import java.util.Set;
  81. import java.util.TreeSet;
  82. import java.util.Map.Entry;
  83. import java.util.concurrent.atomic.AtomicInteger;
  84. import java.util.concurrent.Future;
  85. import java.util.logging.Level;
  86. import java.util.logging.Logger;
  87. import javax.management.timer.Timer;
  88. import javax.servlet.ServletException;
  89. import org.kohsuke.stapler.HttpResponse;
  90. import org.kohsuke.stapler.HttpResponses;
  91. import org.kohsuke.stapler.export.Exported;
  92. import org.kohsuke.stapler.export.ExportedBean;
  93. import com.thoughtworks.xstream.XStream;
  94. import com.thoughtworks.xstream.converters.basic.AbstractSingleValueConverter;
  95. import java.util.HashSet;
  96. /**
  97. * Build queue.
  98. *
  99. * <p>
  100. * This class implements the core scheduling logic. {@link Task} represents the executable
  101. * task that are placed in the queue. While in the queue, it's wrapped into {@link Item}
  102. * so that we can keep track of additional data used for deciding what to exeucte when.
  103. *
  104. * <p>
  105. * Items in queue goes through several stages, as depicted below:
  106. * <pre>
  107. * (enter) --> waitingList --+--> blockedProjects
  108. * | ^
  109. * | |
  110. * | v
  111. * +--> buildables ---> pending ---> (executed)
  112. * </pre>
  113. *
  114. * <p>
  115. * In addition, at any stage, an item can be removed from the queue (for example, when the user
  116. * cancels a job in the queue.) See the corresponding field for their exact meanings.
  117. *
  118. * @author Kohsuke Kawaguchi
  119. */
  120. @ExportedBean
  121. public class Queue extends ResourceController implements Saveable {
  122. /**
  123. * Items that are waiting for its quiet period to pass.
  124. *
  125. * <p>
  126. * This consists of {@link Item}s that cannot be run yet
  127. * because its time has not yet come.
  128. */
  129. private final Set<WaitingItem> waitingList = new TreeSet<WaitingItem>();
  130. /**
  131. * {@link Task}s that can be built immediately
  132. * but blocked because another build is in progress,
  133. * required {@link Resource}s are not available, or otherwise blocked
  134. * by {@link Task#isBuildBlocked()}.
  135. */
  136. private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
  137. /**
  138. * {@link Task}s that can be built immediately
  139. * that are waiting for available {@link Executor}.
  140. * This list is sorted in such a way that earlier items are built earlier.
  141. */
  142. private final ItemList<BuildableItem> buildables = new ItemList<BuildableItem>();
  143. /**
  144. * {@link Task}s that are being handed over to the executor, but execution
  145. * has not started yet.
  146. */
  147. private final ItemList<BuildableItem> pendings = new ItemList<BuildableItem>();
  148. /**
  149. * Data structure created for each idle {@link Executor}.
  150. * This is a job offer from the queue to an executor.
  151. *
  152. * <p>
  153. * An idle executor (that calls {@link Queue#pop()} creates
  154. * a new {@link JobOffer} and gets itself {@linkplain Queue#parked parked},
  155. * and we'll eventually hand out an {@link #workUnit} to build.
  156. */
  157. public class JobOffer extends MappingWorksheet.ExecutorSlot {
  158. public final Executor executor;
  159. /**
  160. * Used to wake up an executor, when it has an offered
  161. * {@link Project} to build.
  162. */
  163. private final OneShotEvent event = new OneShotEvent(Queue.this);
  164. /**
  165. * The work unit that this {@link Executor} is going to handle.
  166. * (Or null, in which case event is used to trigger a queue maintenance.)
  167. */
  168. private WorkUnit workUnit;
  169. private JobOffer(Executor executor) {
  170. this.executor = executor;
  171. }
  172. @Override
  173. protected void set(WorkUnit p) {
  174. assert this.workUnit == null;
  175. this.workUnit = p;
  176. event.signal();
  177. }
  178. @Override
  179. public Executor getExecutor() {
  180. return executor;
  181. }
  182. /**
  183. * Verifies that the {@link Executor} represented by this object is capable of executing the given task.
  184. */
  185. public boolean canTake(Task task) {
  186. Node node = getNode();
  187. if (node==null) return false; // this executor is about to die
  188. if(node.canTake(task)!=null)
  189. return false; // this node is not able to take the task
  190. for (QueueTaskDispatcher d : QueueTaskDispatcher.all())
  191. if (d.canTake(node,task)!=null)
  192. return false;
  193. return isAvailable();
  194. }
  195. /**
  196. * Is this executor ready to accept some tasks?
  197. */
  198. public boolean isAvailable() {
  199. return workUnit == null && !executor.getOwner().isOffline() && executor.getOwner().isAcceptingTasks();
  200. }
  201. public Node getNode() {
  202. return executor.getOwner().getNode();
  203. }
  204. public boolean isNotExclusive() {
  205. return getNode().getMode() == Mode.NORMAL;
  206. }
  207. }
  208. /**
  209. * The executors that are currently waiting for a job to run.
  210. */
  211. private final Map<Executor,JobOffer> parked = new HashMap<Executor,JobOffer>();
  212. private volatile transient LoadBalancer loadBalancer;
  213. private volatile transient QueueSorter sorter;
  214. public Queue(LoadBalancer loadBalancer) {
  215. this.loadBalancer = loadBalancer.sanitize();
  216. // if all the executors are busy doing something, then the queue won't be maintained in
  217. // timely fashion, so use another thread to make sure it happens.
  218. new MaintainTask(this);
  219. }
  220. public LoadBalancer getLoadBalancer() {
  221. return loadBalancer;
  222. }
  223. public void setLoadBalancer(LoadBalancer loadBalancer) {
  224. if(loadBalancer==null) throw new IllegalArgumentException();
  225. this.loadBalancer = loadBalancer;
  226. }
  227. public QueueSorter getSorter() {
  228. return sorter;
  229. }
  230. public void setSorter(QueueSorter sorter) {
  231. this.sorter = sorter;
  232. }
  233. /**
  234. * Loads the queue contents that was {@link #save() saved}.
  235. */
  236. public synchronized void load() {
  237. try {
  238. // first try the old format
  239. File queueFile = getQueueFile();
  240. if (queueFile.exists()) {
  241. BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(queueFile)));
  242. String line;
  243. while ((line = in.readLine()) != null) {
  244. AbstractProject j = Hudson.getInstance().getItemByFullName(line, AbstractProject.class);
  245. if (j != null)
  246. j.scheduleBuild();
  247. }
  248. in.close();
  249. // discard the queue file now that we are done
  250. queueFile.delete();
  251. } else {
  252. queueFile = getXMLQueueFile();
  253. if (queueFile.exists()) {
  254. List list = (List) new XmlFile(XSTREAM, queueFile).read();
  255. int maxId = 0;
  256. for (Object o : list) {
  257. if (o instanceof Task) {
  258. // backward compatibility
  259. schedule((Task)o, 0);
  260. } else if (o instanceof Item) {
  261. Item item = (Item)o;
  262. if(item.task==null)
  263. continue; // botched persistence. throw this one away
  264. maxId = Math.max(maxId, item.id);
  265. if (item instanceof WaitingItem) {
  266. waitingList.add((WaitingItem) item);
  267. } else if (item instanceof BlockedItem) {
  268. blockedProjects.put(item.task, (BlockedItem) item);
  269. } else if (item instanceof BuildableItem) {
  270. buildables.add((BuildableItem) item);
  271. } else {
  272. throw new IllegalStateException("Unknown item type! " + item);
  273. }
  274. } // this conveniently ignores null
  275. }
  276. WaitingItem.COUNTER.set(maxId);
  277. // I just had an incident where all the executors are dead at AbstractProject._getRuns()
  278. // because runs is null. Debugger revealed that this is caused by a MatrixConfiguration
  279. // object that doesn't appear to be de-serialized properly.
  280. // I don't know how this problem happened, but to diagnose this problem better
  281. // when it happens again, save the old queue file for introspection.
  282. File bk = new File(queueFile.getPath() + ".bak");
  283. bk.delete();
  284. queueFile.renameTo(bk);
  285. queueFile.delete();
  286. }
  287. }
  288. } catch (IOException e) {
  289. LOGGER.log(Level.WARNING, "Failed to load the queue file " + getXMLQueueFile(), e);
  290. }
  291. }
  292. /**
  293. * Persists the queue contents to the disk.
  294. */
  295. public synchronized void save() {
  296. if(BulkChange.contains(this)) return;
  297. // write out the tasks on the queue
  298. ArrayList<Queue.Item> items = new ArrayList<Queue.Item>();
  299. for (Item item: getItems()) {
  300. if(item.task instanceof TransientTask) continue;
  301. items.add(item);
  302. }
  303. try {
  304. XmlFile queueFile = new XmlFile(XSTREAM, getXMLQueueFile());
  305. queueFile.write(items);
  306. SaveableListener.fireOnChange(this, queueFile);
  307. } catch (IOException e) {
  308. LOGGER.log(Level.WARNING, "Failed to write out the queue file " + getXMLQueueFile(), e);
  309. }
  310. }
  311. /**
  312. * Wipes out all the items currently in the queue, as if all of them are cancelled at once.
  313. */
  314. @CLIMethod(name="clear-queue")
  315. public synchronized void clear() {
  316. Hudson.getInstance().checkPermission(Hudson.ADMINISTER);
  317. for (WaitingItem i : waitingList)
  318. i.onCancelled();
  319. waitingList.clear();
  320. blockedProjects.cancelAll();
  321. buildables.cancelAll();
  322. scheduleMaintenance();
  323. }
  324. /**
  325. * Called from queue.jelly.
  326. */
  327. public HttpResponse doClearQueue() throws IOException, ServletException {
  328. Hudson.getInstance().getQueue().clear();
  329. return HttpResponses.forwardToPreviousPage();
  330. }
  331. private File getQueueFile() {
  332. return new File(Hudson.getInstance().getRootDir(), "queue.txt");
  333. }
  334. /*package*/ File getXMLQueueFile() {
  335. return new File(Hudson.getInstance().getRootDir(), "queue.xml");
  336. }
  337. /**
  338. * @deprecated as of 1.311
  339. * Use {@link #schedule(AbstractProject)}
  340. */
  341. public boolean add(AbstractProject p) {
  342. return schedule(p)!=null;
  343. }
  344. /**
  345. * Schedule a new build for this project.
  346. *
  347. * @return true if the project is actually added to the queue.
  348. * false if the queue contained it and therefore the add()
  349. * was noop
  350. */
  351. public WaitingItem schedule(AbstractProject p) {
  352. return schedule(p, p.getQuietPeriod());
  353. }
  354. /**
  355. * Schedules a new build with a custom quiet period.
  356. *
  357. * <p>
  358. * Left for backward compatibility with &lt;1.114.
  359. *
  360. * @since 1.105
  361. * @deprecated as of 1.311
  362. * Use {@link #schedule(Task, int)}
  363. */
  364. public boolean add(AbstractProject p, int quietPeriod) {
  365. return schedule(p, quietPeriod)!=null;
  366. }
  367. /**
  368. * Schedules an execution of a task.
  369. *
  370. * @param actions
  371. * These actions can be used for associating information scoped to a particular build, to
  372. * the task being queued. Upon the start of the build, these {@link Action}s will be automatically
  373. * added to the {@link Run} object, and hence avaialable to everyone.
  374. * For the convenience of the caller, this list can contain null, and those will be silently ignored.
  375. * @since 1.311
  376. * @return
  377. * null if this task is already in the queue and therefore the add operation was no-op.
  378. * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
  379. * is that such {@link Item} only captures the state of the item at a particular moment,
  380. * and by the time you inspect the object, some of its information can be already stale.
  381. *
  382. * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
  383. */
  384. public synchronized WaitingItem schedule(Task p, int quietPeriod, List<Action> actions) {
  385. // remove nulls
  386. actions = new ArrayList<Action>(actions);
  387. for (Iterator<Action> itr = actions.iterator(); itr.hasNext();) {
  388. Action a = itr.next();
  389. if (a==null) itr.remove();
  390. }
  391. for(QueueDecisionHandler h : QueueDecisionHandler.all())
  392. if (!h.shouldSchedule(p, actions))
  393. return null; // veto
  394. return scheduleInternal(p, quietPeriod, actions);
  395. }
  396. /**
  397. * Schedules an execution of a task.
  398. *
  399. * @since 1.311
  400. * @return
  401. * null if this task is already in the queue and therefore the add operation was no-op.
  402. * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue
  403. * is that such {@link Item} only captures the state of the item at a particular moment,
  404. * and by the time you inspect the object, some of its information can be already stale.
  405. *
  406. * That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, etc.
  407. */
  408. private synchronized WaitingItem scheduleInternal(Task p, int quietPeriod, List<Action> actions) {
  409. Calendar due = new GregorianCalendar();
  410. due.add(Calendar.SECOND, quietPeriod);
  411. // Do we already have this task in the queue? Because if so, we won't schedule a new one.
  412. List<Item> duplicatesInQueue = new ArrayList<Item>();
  413. for(Item item : getItems(p)) {
  414. boolean shouldScheduleItem = false;
  415. for (QueueAction action: item.getActions(QueueAction.class)) {
  416. shouldScheduleItem |= action.shouldSchedule(actions);
  417. }
  418. for (QueueAction action: Util.filter(actions,QueueAction.class)) {
  419. shouldScheduleItem |= action.shouldSchedule(item.getActions());
  420. }
  421. if(!shouldScheduleItem) {
  422. duplicatesInQueue.add(item);
  423. }
  424. }
  425. if (duplicatesInQueue.isEmpty()) {
  426. LOGGER.fine(p.getFullDisplayName() + " added to queue");
  427. // put the item in the queue
  428. WaitingItem added = new WaitingItem(due,p,actions);
  429. waitingList.add(added);
  430. scheduleMaintenance(); // let an executor know that a new item is in the queue.
  431. return added;
  432. }
  433. LOGGER.fine(p.getFullDisplayName() + " is already in the queue");
  434. // but let the actions affect the existing stuff.
  435. for(Item item : duplicatesInQueue) {
  436. for(FoldableAction a : Util.filter(actions,FoldableAction.class)) {
  437. a.foldIntoExisting(item, p, actions);
  438. }
  439. }
  440. boolean queueUpdated = false;
  441. for(WaitingItem wi : Util.filter(duplicatesInQueue,WaitingItem.class)) {
  442. if(quietPeriod<=0) {
  443. // the user really wants to build now, and they mean NOW.
  444. // so let's pull in the timestamp if we can.
  445. if (wi.timestamp.before(due))
  446. continue;
  447. } else {
  448. // otherwise we do the normal quiet period implementation
  449. if (wi.timestamp.after(due))
  450. continue;
  451. // quiet period timer reset. start the period over again
  452. }
  453. // waitingList is sorted, so when we change a timestamp we need to maintain order
  454. waitingList.remove(wi);
  455. wi.timestamp = due;
  456. waitingList.add(wi);
  457. queueUpdated=true;
  458. }
  459. if (queueUpdated) scheduleMaintenance();
  460. return null;
  461. }
  462. /**
  463. * @deprecated as of 1.311
  464. * Use {@link #schedule(Task, int)}
  465. */
  466. public synchronized boolean add(Task p, int quietPeriod) {
  467. return schedule(p, quietPeriod)!=null;
  468. }
  469. public synchronized WaitingItem schedule(Task p, int quietPeriod) {
  470. return schedule(p, quietPeriod, new Action[0]);
  471. }
  472. /**
  473. * @deprecated as of 1.311
  474. * Use {@link #schedule(Task, int, Action...)}
  475. */
  476. public synchronized boolean add(Task p, int quietPeriod, Action... actions) {
  477. return schedule(p, quietPeriod, actions)!=null;
  478. }
  479. /**
  480. * Convenience wrapper method around {@link #schedule(Task, int, List)}
  481. */
  482. public synchronized WaitingItem schedule(Task p, int quietPeriod, Action... actions) {
  483. return schedule(p, quietPeriod, Arrays.asList(actions));
  484. }
  485. /**
  486. * Cancels the item in the queue. If the item is scheduled more than once, cancels the first occurrence.
  487. *
  488. * @return true if the project was indeed in the queue and was removed.
  489. * false if this was no-op.
  490. */
  491. public synchronized boolean cancel(Task p) {
  492. LOGGER.fine("Cancelling " + p.getFullDisplayName());
  493. for (Iterator<WaitingItem> itr = waitingList.iterator(); itr.hasNext();) {
  494. Item item = itr.next();
  495. if (item.task.equals(p)) {
  496. itr.remove();
  497. item.onCancelled();
  498. return true;
  499. }
  500. }
  501. // use bitwise-OR to make sure that both branches get evaluated all the time
  502. return blockedProjects.cancel(p)!=null | buildables.cancel(p)!=null;
  503. }
  504. public synchronized boolean cancel(Item item) {
  505. LOGGER.fine("Cancelling " + item.task.getFullDisplayName() + " item#" + item.id);
  506. // use bitwise-OR to make sure that all the branches get evaluated all the time
  507. boolean r = (item instanceof WaitingItem && waitingList.remove(item)) | blockedProjects.remove(item) | buildables.remove(item);
  508. if(r)
  509. item.onCancelled();
  510. return r;
  511. }
  512. public synchronized boolean isEmpty() {
  513. return waitingList.isEmpty() && blockedProjects.isEmpty() && buildables.isEmpty() && pendings.isEmpty();
  514. }
  515. private synchronized WaitingItem peek() {
  516. return waitingList.iterator().next();
  517. }
  518. /**
  519. * Gets a snapshot of items in the queue.
  520. *
  521. * Generally speaking the array is sorted such that the items that are most likely built sooner are
  522. * at the end.
  523. */
  524. @Exported(inline=true)
  525. public synchronized Item[] getItems() {
  526. Item[] r = new Item[waitingList.size() + blockedProjects.size() + buildables.size() + pendings.size()];
  527. waitingList.toArray(r);
  528. int idx = waitingList.size();
  529. for (BlockedItem p : blockedProjects.values())
  530. r[idx++] = p;
  531. for (BuildableItem p : reverse(buildables.values()))
  532. r[idx++] = p;
  533. for (BuildableItem p : reverse(pendings.values()))
  534. r[idx++] = p;
  535. return r;
  536. }
  537. public synchronized Item getItem(int id) {
  538. for (Item item: waitingList) if (item.id == id) return item;
  539. for (Item item: blockedProjects) if (item.id == id) return item;
  540. for (Item item: buildables) if (item.id == id) return item;
  541. for (Item item: pendings) if (item.id == id) return item;
  542. return null;
  543. }
  544. /**
  545. * Gets all the {@link BuildableItem}s that are waiting for an executor in the given {@link Computer}.
  546. */
  547. public synchronized List<BuildableItem> getBuildableItems(Computer c) {
  548. List<BuildableItem> result = new ArrayList<BuildableItem>();
  549. _getBuildableItems(c, buildables, result);
  550. _getBuildableItems(c, pendings, result);
  551. return result;
  552. }
  553. private void _getBuildableItems(Computer c, ItemList<BuildableItem> col, List<BuildableItem> result) {
  554. Node node = c.getNode();
  555. for (BuildableItem p : col.values()) {
  556. if (node.canTake(p.task) == null)
  557. result.add(p);
  558. }
  559. }
  560. /**
  561. * Gets the snapshot of all {@link BuildableItem}s.
  562. */
  563. public synchronized List<BuildableItem> getBuildableItems() {
  564. ArrayList<BuildableItem> r = new ArrayList<BuildableItem>(buildables.values());
  565. r.addAll(pendings.values());
  566. return r;
  567. }
  568. /**
  569. * Gets the snapshot of all {@link BuildableItem}s.
  570. */
  571. public synchronized List<BuildableItem> getPendingItems() {
  572. return new ArrayList<BuildableItem>(pendings.values());
  573. }
  574. synchronized Set<Task> getUnblockedQueuedTasks() {
  575. Set<Task> unblockedQueuedTasks = new HashSet<Task>();
  576. for (Item item: waitingList) {
  577. unblockedQueuedTasks.add(item.task);
  578. }
  579. for (Item item: pendings) {
  580. unblockedQueuedTasks.add(item.task);
  581. }
  582. for (Item item: buildables) {
  583. unblockedQueuedTasks.add(item.task);
  584. }
  585. return unblockedQueuedTasks;
  586. }
  587. /**
  588. * Is the given task currently pending execution?
  589. */
  590. public synchronized boolean isPending(Task t) {
  591. for (BuildableItem i : pendings)
  592. if (i.task.equals(t))
  593. return true;
  594. return false;
  595. }
  596. /**
  597. * How many {@link BuildableItem}s are assigned for the given label?
  598. */
  599. public synchronized int countBuildableItemsFor(Label l) {
  600. int r = 0;
  601. for (BuildableItem bi : buildables.values())
  602. if(bi.task.getAssignedLabel()==l)
  603. r++;
  604. for (BuildableItem bi : pendings.values())
  605. if(bi.task.getAssignedLabel()==l)
  606. r++;
  607. return r;
  608. }
  609. /**
  610. * Gets the information about the queue item for the given project.
  611. *
  612. * @return null if the project is not in the queue.
  613. */
  614. public synchronized Item getItem(Task t) {
  615. BlockedItem bp = blockedProjects.get(t);
  616. if (bp!=null)
  617. return bp;
  618. BuildableItem bi = buildables.get(t);
  619. if(bi!=null)
  620. return bi;
  621. bi = pendings.get(t);
  622. if(bi!=null)
  623. return bi;
  624. for (Item item : waitingList) {
  625. if (item.task == t)
  626. return item;
  627. }
  628. return null;
  629. }
  630. /**
  631. * Gets the information about the queue item for the given project.
  632. *
  633. * @return null if the project is not in the queue.
  634. */
  635. public synchronized List<Item> getItems(Task t) {
  636. List<Item> result =new ArrayList<Item>();
  637. result.addAll(blockedProjects.getAll(t));
  638. result.addAll(buildables.getAll(t));
  639. result.addAll(pendings.getAll(t));
  640. for (Item item : waitingList) {
  641. if (item.task == t)
  642. result.add(item);
  643. }
  644. return result;
  645. }
  646. /**
  647. * Left for backward compatibility.
  648. *
  649. * @see #getItem(Task)
  650. public synchronized Item getItem(AbstractProject p) {
  651. return getItem((Task) p);
  652. }
  653. */
  654. /**
  655. * Returns true if this queue contains the said project.
  656. */
  657. public synchronized boolean contains(Task t) {
  658. if (blockedProjects.containsKey(t) || buildables.containsKey(t) || pendings.containsKey(t))
  659. return true;
  660. for (Item item : waitingList) {
  661. if (item.task == t)
  662. return true;
  663. }
  664. return false;
  665. }
  666. /**
  667. * Called by the executor to fetch something to build next.
  668. * <p>
  669. * This method blocks until a next project becomes buildable.
  670. */
  671. public synchronized WorkUnit pop() throws InterruptedException {
  672. final Executor exec = Executor.currentExecutor();
  673. if (exec instanceof OneOffExecutor) {
  674. OneOffExecutor ooe = (OneOffExecutor) exec;
  675. final WorkUnit wu = ooe.getWorkUnit();
  676. pendings.remove(wu.context.item);
  677. return wu;
  678. }
  679. try {
  680. while (true) {
  681. final JobOffer offer = new JobOffer(exec);
  682. long sleep = -1;
  683. // consider myself parked
  684. assert !parked.containsKey(exec);
  685. parked.put(exec, offer);
  686. // reuse executor thread to do a queue maintenance.
  687. // at the end of this we get all the buildable jobs
  688. // in the buildables field.
  689. maintain();
  690. // allocate buildable jobs to executors
  691. Iterator<BuildableItem> itr = buildables.iterator();
  692. while (itr.hasNext()) {
  693. BuildableItem p = itr.next();
  694. // one last check to make sure this build is not blocked.
  695. if (isBuildBlocked(p.task)) {
  696. itr.remove();
  697. blockedProjects.put(p.task,new BlockedItem(p));
  698. continue;
  699. }
  700. List<JobOffer> candidates = new ArrayList<JobOffer>(parked.size());
  701. for (JobOffer j : parked.values())
  702. if(j.canTake(p.task))
  703. candidates.add(j);
  704. MappingWorksheet ws = new MappingWorksheet(p, candidates);
  705. Mapping m = loadBalancer.map(p.task, ws);
  706. if (m == null)
  707. // if we couldn't find the executor that fits,
  708. // just leave it in the buildables list and
  709. // check if we can execute other projects
  710. continue;
  711. // found a matching executor. use it.
  712. WorkUnitContext wuc = new WorkUnitContext(p);
  713. m.execute(wuc);
  714. itr.remove();
  715. if (!wuc.getWorkUnits().isEmpty())
  716. pendings.add(p);
  717. }
  718. // we went over all the buildable projects and awaken
  719. // all the executors that got work to do. now, go to sleep
  720. // until this thread is awakened. If this executor assigned a job to
  721. // itself above, the block method will return immediately.
  722. if (!waitingList.isEmpty()) {
  723. // wait until the first item in the queue is due
  724. sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis();
  725. if (sleep < 100) sleep = 100; // avoid wait(0)
  726. }
  727. if (sleep == -1)
  728. offer.event.block();
  729. else
  730. offer.event.block(sleep);
  731. // retract the offer object
  732. assert parked.get(exec) == offer;
  733. parked.remove(exec);
  734. // am I woken up because I have a project to build?
  735. if (offer.workUnit != null) {
  736. // if so, just build it
  737. LOGGER.fine("Pop returning " + offer.workUnit + " for " + exec.getName());
  738. // TODO: I think this has to be done by the last executor that leaves the pop(), not by main executor
  739. if (offer.workUnit.isMainWork())
  740. pendings.remove(offer.workUnit.context.item);
  741. return offer.workUnit;
  742. }
  743. // otherwise run a queue maintenance
  744. }
  745. } finally {
  746. // remove myself from the parked list
  747. JobOffer offer = parked.remove(exec);
  748. if (offer != null && offer.workUnit != null) {
  749. // we are already assigned a project, but now we can't handle it.
  750. offer.workUnit.context.abort(new AbortException());
  751. }
  752. // since this executor might have been chosen for
  753. // maintenance, schedule another one. Worst case
  754. // we'll just run a pointless maintenance, and that's
  755. // fine.
  756. scheduleMaintenance();
  757. }
  758. }
  759. /**
  760. * Checks the queue and runs anything that can be run.
  761. *
  762. * <p>
  763. * When conditions are changed, this method should be invoked.
  764. * <p>
  765. * This wakes up one {@link Executor} so that it will maintain a queue.
  766. */
  767. public synchronized void scheduleMaintenance() {
  768. // this code assumes that after this method is called
  769. // no more executors will be offered job except by
  770. // the pop() code.
  771. for (Entry<Executor, JobOffer> av : parked.entrySet()) {
  772. if (av.getValue().workUnit == null) {
  773. av.getValue().event.signal();
  774. return;
  775. }
  776. }
  777. }
  778. /**
  779. * Checks if the given task is blocked.
  780. */
  781. private boolean isBuildBlocked(Task t) {
  782. return t.isBuildBlocked() || !canRun(t.getResourceList());
  783. }
  784. /**
  785. * Make sure we don't queue two tasks of the same project to be built
  786. * unless that project allows concurrent builds.
  787. */
  788. private boolean allowNewBuildableTask(Task t) {
  789. try {
  790. if (t.isConcurrentBuild())
  791. return true;
  792. } catch (AbstractMethodError e) {
  793. // earlier versions don't have the "isConcurrentBuild" method, so fall back gracefully
  794. }
  795. return !buildables.containsKey(t) && !pendings.containsKey(t);
  796. }
  797. /**
  798. * Queue maintenance.
  799. * <p>
  800. * Move projects between {@link #waitingList}, {@link #blockedProjects}, and {@link #buildables}
  801. * appropriately.
  802. */
  803. public synchronized void maintain() {
  804. if (LOGGER.isLoggable(Level.FINE))
  805. LOGGER.fine("Queue maintenance started " + this);
  806. // blocked -> buildable
  807. Iterator<BlockedItem> itr = blockedProjects.values().iterator();
  808. while (itr.hasNext()) {
  809. BlockedItem p = itr.next();
  810. if (!isBuildBlocked(p.task) && allowNewBuildableTask(p.task)) {
  811. // ready to be executed
  812. LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked");
  813. itr.remove();
  814. makeBuildable(new BuildableItem(p));
  815. }
  816. }
  817. while (!waitingList.isEmpty()) {
  818. WaitingItem top = peek();
  819. if (!top.timestamp.before(new GregorianCalendar()))
  820. return; // finished moving all ready items from queue
  821. waitingList.remove(top);
  822. Task p = top.task;
  823. if (!isBuildBlocked(p) && allowNewBuildableTask(p)) {
  824. // ready to be executed immediately
  825. LOGGER.fine(p.getFullDisplayName() + " ready to build");
  826. makeBuildable(new BuildableItem(top));
  827. } else {
  828. // this can't be built now because another build is in progress
  829. // set this project aside.
  830. LOGGER.fine(p.getFullDisplayName() + " is blocked");
  831. blockedProjects.put(p,new BlockedItem(top));
  832. }
  833. }
  834. final QueueSorter s = sorter;
  835. if (s != null)
  836. s.sortBuildableItems(buildables);
  837. }
  838. private void makeBuildable(BuildableItem p) {
  839. if(Hudson.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask && !ifBlockedByHudsonShutdown(p.task)) {
  840. ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() {
  841. public String hash(Node node) {
  842. return node.getNodeName();
  843. }
  844. });
  845. Hudson h = Hudson.getInstance();
  846. hash.add(h, h.getNumExecutors()*100);
  847. for (Node n : h.getNodes())
  848. hash.add(n,n.getNumExecutors()*100);
  849. Label lbl = p.task.getAssignedLabel();
  850. for (Node n : hash.list(p.task.getFullDisplayName())) {
  851. Computer c = n.toComputer();
  852. if (c==null || c.isOffline()) continue;
  853. if (lbl!=null && !lbl.contains(n)) continue;
  854. c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
  855. pendings.add(p);
  856. return;
  857. }
  858. // if the execution get here, it means we couldn't schedule it anywhere.
  859. // so do the scheduling like other normal jobs.
  860. }
  861. buildables.put(p.task,p);
  862. }
  863. public static boolean ifBlockedByHudsonShutdown(Task task) {
  864. return Hudson.getInstance().isQuietingDown() && !(task instanceof NonBlockingTask);
  865. }
  866. public Api getApi() {
  867. return new Api(this);
  868. }
  869. /**
  870. * Marks {@link Task}s that are not persisted.
  871. * @since 1.311
  872. */
  873. public interface TransientTask extends Task {}
  874. /**
  875. * Marks {@link Task}s that do not consume {@link Executor}.
  876. * @see OneOffExecutor
  877. * @since 1.318
  878. */
  879. public interface FlyweightTask extends Task {}
  880. /**
  881. * Marks {@link Task}s that are not affected by the {@linkplain Hudson#isQuietingDown()} quieting down},
  882. * because these tasks keep other tasks executing.
  883. *
  884. * @since 1.336
  885. */
  886. public interface NonBlockingTask extends Task {}
  887. /**
  888. * Task whose execution is controlled by the queue.
  889. *
  890. * <p>
  891. * {@link #equals(Object) Value equality} of {@link Task}s is used
  892. * to collapse two tasks into one. This is used to avoid infinite
  893. * queue backlog.
  894. *
  895. * <p>
  896. * Pending {@link Task}s are persisted when Hudson shuts down, so
  897. * it needs to be persistable via XStream. To create a non-persisted
  898. * transient Task, extend {@link TransientTask} marker interface.
  899. *
  900. * <p>
  901. * Plugins are encouraged to extend from {@link AbstractQueueTask}
  902. * instead of implementing this interface directly, to maintain
  903. * compatibility with future changes to this interface.
  904. *
  905. * <p>
  906. * For historical reasons, {@link Task} object by itself
  907. * also represents the "primary" sub-task (and as implied by this
  908. * design, a {@link Task} must have at least one sub-task.)
  909. * Most of the time, the primary subtask is the only sub task.
  910. */
  911. public interface Task extends ModelObject, SubTask {
  912. /**
  913. * Returns true if the execution should be blocked
  914. * for temporary reasons.
  915. *
  916. * <p>
  917. * Short-hand for {@code getCauseOfBlockage()!=null}.
  918. */
  919. boolean isBuildBlocked();
  920. /**
  921. * @deprecated as of 1.330
  922. * Use {@link CauseOfBlockage#getShortDescription()} instead.
  923. */
  924. String getWhyBlocked();
  925. /**
  926. * If the execution of this task should be blocked for temporary reasons,
  927. * this method returns a non-null object explaining why.
  928. *
  929. * <p>
  930. * Otherwise this method returns null, indicating that the build can proceed right away.
  931. *
  932. * <p>
  933. * This can be used to define mutual exclusion that goes beyond
  934. * {@link #getResourceList()}.
  935. */
  936. CauseOfBlockage getCauseOfBlockage();
  937. /**
  938. * Unique name of this task.
  939. *
  940. * <p>
  941. * This method is no longer used, left here for compatibility. Just return {@link #getDisplayName()}.
  942. */
  943. String getName();
  944. /**
  945. * @see hudson.model.Item#getFullDisplayName()
  946. */
  947. String getFullDisplayName();
  948. /**
  949. * Checks the permission to see if the current user can abort this executable.
  950. * Returns normally from this method if it's OK.
  951. *
  952. * @throws org.acegisecurity.AccessDeniedException if the permission is not granted.
  953. */
  954. void checkAbortPermission();
  955. /**
  956. * Works just like {@link #checkAbortPermission()} except it indicates the status by a return value,
  957. * instead of exception.
  958. */
  959. boolean hasAbortPermission();
  960. /**
  961. * Returns the URL of this task relative to the context root of the application.
  962. *
  963. * <p>
  964. * When the user clicks an item in the queue, this is the page where the user is taken to.
  965. * Hudson expects the current instance to be bound to the URL returned by this method.
  966. *
  967. * @return
  968. * URL that ends with '/'.
  969. */
  970. String getUrl();
  971. /**
  972. * True if the task allows concurrent builds
  973. *
  974. * @since 1.338
  975. */
  976. boolean isConcurrentBuild();
  977. /**
  978. * Obtains the {@link SubTask}s that constitute this task.
  979. *
  980. * <p>
  981. * The collection returned by this method must also contain the primary {@link SubTask}
  982. * represented by this {@link Task} object itself as the first element.
  983. * The returned value is read-only.
  984. *
  985. * <p>
  986. * At least size 1.
  987. *
  988. * <p>
  989. * Since this is a newly added method, the invocation may results in {@link AbstractMethodError}.
  990. * Use {@link Tasks#getSubTasksOf(Task)} that avoids this.
  991. *
  992. * @since 1.377
  993. */
  994. Collection<? extends SubTask> getSubTasks();
  995. }
  996. /**
  997. * Represents the real meat of the computation run by {@link Executor}.
  998. *
  999. * <h2>Views</h2>
  1000. * <p>
  1001. * Implementation must have <tt>executorCell.jelly</tt>, which is
  1002. * used to render the HTML that indicates this executable is executing.
  1003. */
  1004. public interface Executable extends Runnable {
  1005. /**
  1006. * Task from which this executable was created.
  1007. * Never null.
  1008. *
  1009. * <p>
  1010. * Since this method went through a signature change in 1.377, the invocation may results in
  1011. * {@link AbstractMethodError}.
  1012. * Use {@link Executables#getParentOf(Executable)} that avoids this.
  1013. */
  1014. SubTask getParent();
  1015. /**
  1016. * Called by {@link Executor} to perform the task
  1017. */
  1018. void run();
  1019. /**
  1020. * Estimate of how long will it take to execute this executable.
  1021. * Measured in milliseconds.
  1022. *
  1023. * Please, consider using {@link Executables#getEstimatedDurationFor(Executable)}
  1024. * to protected against AbstractMethodErrors!
  1025. *
  1026. * @return -1 if it's impossible to estimate.
  1027. * @since 1.383
  1028. */
  1029. long getEstimatedDuration();
  1030. /**
  1031. * Used to render the HTML. Should be a human readable text of what this executable is.
  1032. */
  1033. @Override String toString();
  1034. }
  1035. /**
  1036. * Item in a queue.
  1037. */
  1038. @ExportedBean(defaultVisibility = 999)
  1039. public static abstract class Item extends Actionable {
  1040. /**
  1041. * VM-wide unique ID that tracks the {@link Task} as it moves through different stages
  1042. * in the queue (each represented by different subtypes of {@link Item}.
  1043. */
  1044. //TODO: review and check whether we can do it private
  1045. public final int id;
  1046. /**
  1047. * Project to be built.
  1048. */
  1049. //TODO: review and check whether we can do it private
  1050. @Exported
  1051. public final Task task;
  1052. private /*almost final*/ transient FutureImpl future;
  1053. /**
  1054. * Build is blocked because another build is in progress,
  1055. * required {@link Resource}s are not available, or otherwise blocked
  1056. * by {@link Task#isBuildBlocked()}.
  1057. */
  1058. @Exported
  1059. public boolean isBlocked() { return this instanceof BlockedItem; }
  1060. /**
  1061. * Build is waiting the executor to become available.
  1062. * This flag is only used in {@link Queue#getItems()} for
  1063. * 'pseudo' items that are actually not really in the queue.
  1064. */
  1065. @Exported
  1066. public boolean isBuildable() { return this instanceof BuildableItem; }
  1067. /**
  1068. * True if the item is starving for an executor for too long.
  1069. */
  1070. @Exported
  1071. public boolean isStuck() { return false; }
  1072. /**
  1073. * Can be used to wait for the completion (either normal, abnormal, or cancellation) of the {@link Task}.
  1074. * <p>
  1075. * Just like {@link #id}, the same object tracks various stages of the queue.
  1076. */
  1077. public Future<Executable> getFuture() { return future; }
  1078. /**
  1079. * Convenience method that returns a read only view of the {@link Cause}s associated with this item in the queue.
  1080. *
  1081. * @return can be empty but never null
  1082. * @since 1.343
  1083. */
  1084. public final List<Cause> getCauses() {
  1085. CauseAction ca = getAction(CauseAction.class);
  1086. if (ca!=null)
  1087. return Collections.unmodifiableList(ca.getCauses());
  1088. return Collections.emptyList();
  1089. }
  1090. protected Item(Task task, List<Action> actions, int id, FutureImpl future) {
  1091. this.task = task;
  1092. this.id = id;
  1093. this.future = future;
  1094. for (Action action: actions) addAction(action);
  1095. }
  1096. protected Item(Item item) {
  1097. this(item.task, item.getActions(), item.id, item.future);
  1098. }
  1099. public int getId() {
  1100. return id;
  1101. }
  1102. public Task getTask() {
  1103. return task;
  1104. }
  1105. /**
  1106. * Gets a human-readable status message describing why it's in the queue.
  1107. */
  1108. @Exported
  1109. public final String getWhy() {
  1110. CauseOfBlockage cob = getCauseOfBlockage();
  1111. return cob!=null ? cob.getShortDescription() : null;
  1112. }
  1113. /**
  1114. * Gets an object that describes why this item is in the queue.
  1115. */
  1116. //TODO: review and check whether we can do it private
  1117. public abstract CauseOfBlockage getCauseOfBlockage();
  1118. /**
  1119. * Gets a human-readable message about the parameters of this item
  1120. * @return String
  1121. */
  1122. @Exported
  1123. public String getParams() {
  1124. StringBuilder s = new StringBuilder();
  1125. for(Action action : getActions()) {
  1126. if(action instanceof ParametersAction) {
  1127. ParametersAction pa = (ParametersAction)action;
  1128. for (ParameterValue p : pa.getParameters()) {
  1129. s.append('\n').append(p.getShortDescription());
  1130. }
  1131. }
  1132. }
  1133. return s.toString();
  1134. }
  1135. public boolean hasCancelPermission() {
  1136. return task.hasAbortPermission();
  1137. }
  1138. public String getDisplayName() {
  1139. // TODO Auto-generated method stub
  1140. return null;
  1141. }
  1142. public String getSearchUrl() {
  1143. // TODO Auto-generated method stub
  1144. return null;
  1145. }
  1146. /**
  1147. * Called from queue.jelly.
  1148. */
  1149. public HttpResponse doCancelQueue() throws IOException, ServletException {
  1150. Hudson.getInstance().getQueue().cancel(this);
  1151. return HttpResponses.forwardToPreviousPage();
  1152. }
  1153. /**
  1154. * Participates in the cancellation logic to set the {@link #future} accordingly.
  1155. */
  1156. /*package*/ void onCancelled() {
  1157. future.setAsCancelled();
  1158. }
  1159. private Object readResolve() {
  1160. this.future = new FutureImpl(task);
  1161. return this;
  1162. }
  1163. @Override
  1164. public String toString() {
  1165. return getClass().getName()+':'+task.toString();
  1166. }
  1167. }
  1168. /**
  1169. * An optional interface for actions on Queue.Item.
  1170. * Lets the action cooperate in queue management.
  1171. *
  1172. * @since 1.300-ish.
  1173. */
  1174. public interface QueueAction extends Action {
  1175. /**
  1176. * Returns whether the new item should be scheduled.
  1177. * An action should return true if the associated task is 'different enough' to warrant a separate execution.
  1178. */
  1179. public boolean shouldSchedule(List<Action> actions);
  1180. }
  1181. /**
  1182. * Extension point for deciding if particular job should be scheduled or not.
  1183. *
  1184. * <p>
  1185. * This handler is consulted every time someone tries to submit a task to the queue.
  1186. * If any of the registered handlers returns false, the task will not be added
  1187. * to the queue, and the task will never get executed.
  1188. *
  1189. * <p>
  1190. * This extension point is still a subject to change, as we are seeking more
  1191. * comprehensive Queue pluggability. See HUDSON-2072.
  1192. *
  1193. * @since 1.316
  1194. */
  1195. public static abstract class QueueDecisionHandler implements ExtensionPoint {
  1196. /**
  1197. * Returns whether the new item should be scheduled.
  1198. */
  1199. public abstract boolean shouldSchedule(Task p, List<Action> actions);
  1200. /**
  1201. * All registered {@link QueueDecisionHandler}s
  1202. * @return
  1203. */
  1204. public static ExtensionList<QueueDecisionHandler> all() {
  1205. return Hudson.getInstance().getExtensionList(QueueDecisionHandler.class);
  1206. }
  1207. }
  1208. /**
  1209. * {@link Item} in the {@link Queue#waitingList} stage.
  1210. */
  1211. public static final class WaitingItem extends Item implements Comparable<WaitingItem> {
  1212. private static final AtomicInteger COUNTER = new AtomicInteger(0);
  1213. /**
  1214. * This item can be run after this time.
  1215. */
  1216. //TODO: review and check whether we can do it private
  1217. @Exported
  1218. public Calendar timestamp;
  1219. public Calendar getTimestamp() {
  1220. return timestamp;
  1221. }
  1222. public WaitingItem(Calendar timestamp, Task project, List<Action> actions) {
  1223. super(project, actions, COUNTER.incrementAndGet(), new FutureImpl(project));
  1224. this.timestamp = timestamp;
  1225. }
  1226. public int compareTo(WaitingItem that) {
  1227. int r = this.timestamp.getTime().compareTo(that.timestamp.getTime());
  1228. if (r != 0) return r;
  1229. return this.id - that.id;
  1230. }
  1231. public CauseOfBlockage getCauseOfBlockage() {
  1232. long diff = timestamp.getTimeInMillis() - System.currentTimeMillis();
  1233. if (diff > 0)
  1234. return CauseOfBlockage.fromMessage(Messages._Queue_InQuietPeriod(Util.getTimeSpanString(diff)));
  1235. else
  1236. return CauseOfBlockage.fromMessage(Messages._Queue_Unknown());
  1237. }
  1238. }
  1239. /**
  1240. * Common part between {@link BlockedItem} and {@link BuildableItem}.
  1241. */
  1242. public static abstract class NotWaitingItem extends Item {
  1243. /**
  1244. * When did this job exit the {@link Queue#waitingList} phase?
  1245. */
  1246. @Exported
  1247. public final long buildableStartMilliseconds;
  1248. protected NotWaitingItem(WaitingItem wi) {
  1249. super(wi);
  1250. buildableStartMilliseconds = System.currentTimeMillis();
  1251. }
  1252. protected NotWaitingItem(NotWaitingItem ni) {
  1253. super(ni);
  1254. buildableStartMilliseconds = ni.buildableStartMilliseconds;
  1255. }
  1256. }
  1257. /**
  1258. * {@link Item} in the {@link Queue#blockedProjects} stage.
  1259. */
  1260. public final class BlockedItem extends NotWaitingItem {
  1261. public BlockedItem(WaitingItem wi) {
  1262. super(wi);
  1263. }
  1264. public BlockedItem(NotWaitingItem ni) {
  1265. super(ni);
  1266. }
  1267. public CauseOfBlockage getCauseOfBlockage() {
  1268. ResourceActivity r = getBlockingActivity(task);
  1269. if (r != null) {
  1270. if (r == task) // blocked by itself, meaning another build is in progress
  1271. return CauseOfBlockage.fromMessage(Messages._Queue_InProgress());
  1272. return CauseOfBlockage.fromMessage(Messages._Queue_BlockedBy(r.getDisplayName()));
  1273. }
  1274. return task.getCauseOfBlockage();
  1275. }
  1276. }
  1277. /**
  1278. * {@link Item} in the {@link Queue#buildables} stage.
  1279. */
  1280. public final static class BuildableItem extends NotWaitingItem {
  1281. public BuildableItem(WaitingItem wi) {
  1282. super(wi);
  1283. }
  1284. public BuildableItem(NotWaitingItem ni) {
  1285. super(ni);
  1286. }
  1287. public CauseOfBlockage getCauseOfBlockage() {
  1288. Hudson hudson = Hudson.getInstance();
  1289. if(ifBlockedByHudsonShutdown(task))
  1290. return CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown());
  1291. Label label = task.getAssignedLabel();
  1292. if (hudson.getNodes().isEmpty())
  1293. label = null; // no master/slave. pointless to talk about nodes
  1294. if (label != null) {
  1295. if (label.isOffline()) {
  1296. Set<Node> nodes = label.getNodes();
  1297. if (nodes.size() != 1) return new BecauseLabelIsOffline(label);
  1298. else return new BecauseNodeIsOffline(nodes.iterator().next());
  1299. }
  1300. }
  1301. if(label==null)
  1302. return CauseOfBlockage.fromMessage(Messages._Queue_WaitingForNextAvailableExecutor());
  1303. Set<Node> nodes = label.getNodes();
  1304. if (nodes.size() != 1) return new BecauseLabelIsBusy(label);
  1305. else return new BecauseNodeIsBusy(nodes.iterator().next());
  1306. }
  1307. @Override
  1308. public boolean isStuck() {
  1309. Label label = task.getAssignedLabel();
  1310. if(label!=null && label.isOffline())
  1311. // no executor online to process this job. definitely stuck.
  1312. return true;
  1313. long d = task.getEstimatedDuration();
  1314. long elapsed = System.currentTimeMillis()-buildableStartMilliseconds;
  1315. if(d>=0) {
  1316. // if we were running elsewhere, we would have done this build ten times.
  1317. return elapsed > Math.max(d,60000L)*10;
  1318. } else {
  1319. // more than a day in the queue
  1320. return TimeUnit2.MILLISECONDS.toHours(elapsed)>24;
  1321. }
  1322. }
  1323. }
  1324. private static final Logger LOGGER = Logger.getLogger(Queue.class.getName());
  1325. /**
  1326. * This {@link XStream} instance is used to persist {@link Task}s.
  1327. */
  1328. public static final XStream XSTREAM = new XStream2();
  1329. static {
  1330. XSTREAM.registerConverter(new AbstractSingleValueConverter() {
  1331. @Override
  1332. @SuppressWarnings("unchecked")
  1333. public boolean canConvert(Class klazz) {
  1334. return hudson.model.Item.class.isAssignableFrom(klazz);
  1335. }
  1336. @Override
  1337. public Object fromString(String string) {
  1338. Object item = Hudson.getInstance().getItemByFullName(string);
  1339. if(item==null) throw new NoSuchElementException("No such job exists: "+string);
  1340. return item;
  1341. }
  1342. @Override
  1343. public String toString(Object item) {
  1344. return ((hudson.model.Item) item).getFullName();
  1345. }
  1346. });
  1347. XSTREAM.registerConverter(new AbstractSingleValueConverter() {
  1348. @SuppressWarnings("unchecked")
  1349. @Override
  1350. public boolean canConvert(Class klazz) {
  1351. return Run.class.isAssignableFrom(klazz);
  1352. }
  1353. @Override
  1354. public Object fromString(String string) {
  1355. String[] split = string.split("#");
  1356. String projectName = split[0];
  1357. int buildNumber = Integer.parseInt(split[1]);
  1358. Job<?,?> job = (Job<?,?>) Hudson.getInstance().getItemByFullName(projectName);
  1359. if(job==null) throw new NoSuchElementException("No such job exists: "+projectName);
  1360. Run<?,?> run = job.getBuildByNumber(buildNumber);
  1361. if(run==null) throw new NoSuchElementException("No such build: "+string);
  1362. return run;
  1363. }
  1364. @Override
  1365. public String toString(Object object) {
  1366. Run<?,?> run = (Run<?,?>) object;
  1367. return run.getParent().getFullName() + "#" + run.getNumber();
  1368. }
  1369. });
  1370. }
  1371. /**
  1372. * Regularly invokes {@link Queue#maintain()} and clean itself up when
  1373. * {@link Queue} gets GC-ed.
  1374. */
  1375. private static class MaintainTask extends SafeTimerTask {
  1376. private final WeakReference<Queue> queue;
  1377. MaintainTask(Queue queue) {
  1378. this.queue = new WeakReference<Queue>(queue);
  1379. long interval = 5 * Timer.ONE_SECOND;
  1380. Trigger.timer.schedule(this, interval, interval);
  1381. }
  1382. protected void doRun() {
  1383. Queue q = queue.get();
  1384. if (q != null)
  1385. q.maintain();
  1386. else
  1387. cancel();
  1388. }
  1389. }
  1390. /**
  1391. * {@link ArrayList} of {@link Item} with more convenience methods.
  1392. */
  1393. private static class ItemList<T extends Item> extends ArrayList<T> {
  1394. public T get(Task task) {
  1395. for (T item: this) {
  1396. if (item.task == task) {
  1397. return item;
  1398. }
  1399. }
  1400. return null;
  1401. }
  1402. public List<T> getAll(Task task) {
  1403. List<T> result = new ArrayList<T>();
  1404. for (T item: this) {
  1405. if (item.task == task) {
  1406. result.add(item);
  1407. }
  1408. }
  1409. return result;
  1410. }
  1411. public boolean containsKey(Task task) {
  1412. return get(task) != null;
  1413. }
  1414. public T remove(Task task) {
  1415. Iterator<T> it = iterator();
  1416. while (it.hasNext()) {
  1417. T t = it.next();
  1418. if (t.task == task) {
  1419. it.remove();
  1420. return t;
  1421. }
  1422. }
  1423. return null;
  1424. }
  1425. public void put(Task task, T item) {
  1426. assert item.task == task;
  1427. add(item);
  1428. }
  1429. public ItemList<T> values() {
  1430. return this;
  1431. }
  1432. /**
  1433. * Works like {@link #remove(Task)} but also marks the {@link Item} as cancelled.
  1434. */
  1435. public T cancel(Task p) {
  1436. T x = remove(p);
  1437. if(x!=null) x.onCancelled();
  1438. return x;
  1439. }
  1440. /**
  1441. * Works like {@link #remove(Object)} but also marks the {@link Item} as cancelled.
  1442. */
  1443. public boolean cancel(Item t) {
  1444. boolean r = remove(t);
  1445. if(r) t.onCancelled();
  1446. return r;
  1447. }
  1448. public void cancelAll() {
  1449. for (T t : this)
  1450. t.onCancelled();
  1451. clear();
  1452. }
  1453. }
  1454. @CLIResolver
  1455. public static Queue getInstance() {
  1456. return Hudson.getInstance().getQueue();
  1457. }
  1458. /**
  1459. * Restores the queue content during the start up.
  1460. */
  1461. @Initializer(after=JOB_LOADED)
  1462. public static void init(Hudson h) {
  1463. h.getQueue().load();
  1464. }
  1465. }