diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index a4c12360702333b79eb1bf636a5626dfda489bc6..7d0621bf9be9bc62f812946d35160b2166626308 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -293,7 +293,7 @@ public abstract class Computer extends AbstractModelObject { */ public final long getDemandStartMilliseconds() { long firstDemand = Long.MAX_VALUE; - for (Queue.Item item : Hudson.getInstance().getQueue().getBuildableItems(this)) { + for (Queue.BuildableItem item : Hudson.getInstance().getQueue().getBuildableItems(this)) { firstDemand = Math.min(item.buildableStartMilliseconds, firstDemand); } return firstDemand; diff --git a/core/src/main/java/hudson/model/Queue.java b/core/src/main/java/hudson/model/Queue.java index e59fcc23095ddf3c0879f90378a5961f18f006eb..d70a4410b741f2b3a30a34326bab6c198ac858af 100644 --- a/core/src/main/java/hudson/model/Queue.java +++ b/core/src/main/java/hudson/model/Queue.java @@ -49,36 +49,43 @@ import java.util.logging.Logger; */ public class Queue extends ResourceController { /** - * Items in the queue ordered by {@link Item#timestamp}. + * Items that are waiting for its quiet period to pass. * *
* This consists of {@link Item}s that cannot be run yet
* because its time has not yet come.
*/
- private final Set
+ * Conceptually a set of {@link BlockedItem}, but we often need to look up
+ * {@link BlockedItem} from {@link Task}, so organized as a map.
*/
- private final Set
+ * Conceptually, this is a list of {@link BuildableItem} (FIFO list, not a set, so that
+ * the item doesn't starve in the queue), but we often need to look up
+ * {@link BuildableItem} from {@link Task}, so organized as a {@link LinkedHashMap}.
*/
- private final List
- * It eventually receives a {@link #task} to build.
+ * It eventually receives a {@link #item} to build.
*/
private static class JobOffer {
final Executor executor;
@@ -92,20 +99,20 @@ public class Queue extends ResourceController {
* The project that this {@link Executor} is going to build.
* (Or null, in which case event is used to trigger a queue maintenance.)
*/
- Task task;
+ BuildableItem item;
public JobOffer(Executor executor) {
this.executor = executor;
}
- public void set(Task p) {
- assert this.task == null;
- this.task = p;
+ public void set(BuildableItem p) {
+ assert this.item == null;
+ this.item = p;
event.signal();
}
public boolean isAvailable() {
- return task == null && !executor.getOwner().isOffline();
+ return item == null && !executor.getOwner().isOffline();
}
public Node getNode() {
@@ -206,16 +213,22 @@ public class Queue extends ResourceController {
Calendar due = new GregorianCalendar();
due.add(Calendar.SECOND, quietPeriod);
if (item != null) {
- if (item.timestamp.before(due))
+ if (!(item instanceof WaitingItem))
+ // already in the blocked or buildable stage
+ // no need to requeue
+ return false;
+
+ WaitingItem wi = (WaitingItem) item;
+ if (wi.timestamp.before(due))
return false; // no double queueing
// allow the due date to be pulled in
- item.timestamp = due;
+ wi.timestamp = due;
} else {
LOGGER.fine(p.getName() + " added to queue");
// put the item in the queue
- waitingList.add(new Item(due, p));
+ waitingList.add(new WaitingItem(due,p));
}
scheduleMaintenance(); // let an executor know that a new item is in the queue.
@@ -238,15 +251,14 @@ public class Queue extends ResourceController {
}
}
// use bitwise-OR to make sure that both branches get evaluated all the time
- enterBuildables.remove(p);
- return blockedProjects.remove(p) | buildables.remove(p);
+ return blockedProjects.remove(p)!=null | buildables.remove(p)!=null;
}
public synchronized boolean isEmpty() {
return waitingList.isEmpty() && blockedProjects.isEmpty() && buildables.isEmpty();
}
- private synchronized Item peek() {
+ private synchronized WaitingItem peek() {
return waitingList.iterator().next();
}
@@ -257,35 +269,25 @@ public class Queue extends ResourceController {
Item[] r = new Item[waitingList.size() + blockedProjects.size() + buildables.size()];
waitingList.toArray(r);
int idx = waitingList.size();
- Calendar now = new GregorianCalendar();
- for (Task p : blockedProjects) {
- r[idx++] = new Item(now, p, true, false);
- }
- for (Task p : buildables) {
- if (!enterBuildables.containsKey(p)) {
- enterBuildables.put(p, System.currentTimeMillis());
- }
- r[idx++] = new Item(now, p, false, true, enterBuildables.get(p));
- }
+ for (BlockedItem p : blockedProjects.values())
+ r[idx++] = p;
+ for (BuildableItem p : buildables.values())
+ r[idx++] = p;
return r;
}
- public synchronized Item[] getBuildableItems(Computer c) {
- List
- * This flag is only used in {@link Queue#getItems()} for
- * 'pseudo' items that are actually not really in the queue.
*/
@Exported
- public final boolean isBlocked;
+ public boolean isBlocked() { return this instanceof BlockedItem; }
/**
* Build is waiting the executor to become available.
@@ -704,23 +692,10 @@ public class Queue extends ResourceController {
* 'pseudo' items that are actually not really in the queue.
*/
@Exported
- public final boolean isBuildable;
-
- public Item(Calendar timestamp, Task project) {
- this(timestamp, project, false, false);
- }
+ public boolean isBuildable() { return this instanceof BuildableItem; }
- public Item(Calendar timestamp, Task project, boolean isBlocked, boolean isBuildable) {
- this(timestamp, project, isBlocked, isBuildable, System.currentTimeMillis() + 1000);
- }
-
- public Item(Calendar timestamp, Task project, boolean isBlocked, boolean isBuildable,
- long buildableStartMilliseconds) {
- this.timestamp = timestamp;
+ protected Item(Task project) {
this.task = project;
- this.isBlocked = isBlocked;
- this.isBuildable = isBuildable;
- this.buildableStartMilliseconds = buildableStartMilliseconds;
synchronized (Queue.this) {
this.id = iota++;
}
@@ -730,56 +705,122 @@ public class Queue extends ResourceController {
* Gets a human-readable status message describing why it's in the queu.
*/
@Exported
- public String getWhy() {
- if (isBuildable) {
- Label node = task.getAssignedLabel();
- Hudson hudson = Hudson.getInstance();
- if (hudson.getSlaves().isEmpty())
- node = null; // no master/slave. pointless to talk about nodes
-
- String name = null;
- if (node != null) {
- name = node.getName();
- if (node.isOffline()) {
- if (node.getNodes().size() > 1)
- return "All nodes of label '" + name + "' is offline";
- else
- return name + " is offline";
- }
- }
+ public abstract String getWhy();
- return "Waiting for next available executor" + (name == null ? "" : " on " + name);
- }
+ public boolean hasCancelPermission() {
+ return task.hasAbortPermission();
+ }
+ }
- if (isBlocked) {
- ResourceActivity r = getBlockingActivity(task);
- if (r != null) {
- if (r == task) // blocked by itself, meaning another build is in progress
- return Messages.Queue_InProgress();
- return Messages.Queue_BlockedBy(r.getDisplayName());
- }
- return task.getWhyBlocked();
- }
+ /**
+ * {@link Item} in the {@link Queue#waitingList} stage.
+ */
+ public final class WaitingItem extends Item implements Comparable