提交 8cd99cac 编写于 作者: V Valentina Armenise

[JENKINS-30084] flyweight tasks gets added to the buildable list when no executor is available

上级 f552d48f
...@@ -194,6 +194,17 @@ public class Queue extends ResourceController implements Saveable { ...@@ -194,6 +194,17 @@ public class Queue extends ResourceController implements Saveable {
*/ */
private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>(); private final ItemList<BlockedItem> blockedProjects = new ItemList<BlockedItem>();
/**
* List of type {@link FlyWeightItem}.
*
* <p>
* This consists of {@link Item}s which require one additional executor,
* which is a temporary executor, to manage the execution of its nested tasks.
* This applies to Matrix project.
*/
private final ItemList<FlyWeightItem> flyWeightTasks = new ItemList<FlyWeightItem>();
/** /**
* {@link Task}s that can be built immediately * {@link Task}s that can be built immediately
* that are waiting for available {@link Executor}. * that are waiting for available {@link Executor}.
...@@ -1427,6 +1438,20 @@ public class Queue extends ResourceController implements Saveable { ...@@ -1427,6 +1438,20 @@ public class Queue extends ResourceController implements Saveable {
} }
} }
//this is to solve OSS-192. We iterate on the list of flyweight tasks and execute them
List<FlyWeightItem> flyweightItems = new ArrayList<>(flyWeightTasks.values());
for (FlyWeightItem f : flyweightItems) {
if (!isBuildBlocked(f)) {
// ready to be executed
Runnable r = makeBuildable(new BuildableItem(f));
if (r != null) {
f.leave(this);
r.run();
updateSnapshot();
}
}
}
// waitingList -> buildable/blocked // waitingList -> buildable/blocked
while (!waitingList.isEmpty()) { while (!waitingList.isEmpty()) {
WaitingItem top = peek(); WaitingItem top = peek();
...@@ -1442,7 +1467,8 @@ public class Queue extends ResourceController implements Saveable { ...@@ -1442,7 +1467,8 @@ public class Queue extends ResourceController implements Saveable {
if (r != null) { if (r != null) {
r.run(); r.run();
} else { } else {
new BlockedItem(top).enter(this); new BuildableItem(top).enter(this);
new FlyWeightItem(top).enter(this);
} }
} else { } else {
// this can't be built now because another build is in progress // this can't be built now because another build is in progress
...@@ -1460,6 +1486,9 @@ public class Queue extends ResourceController implements Saveable { ...@@ -1460,6 +1486,9 @@ public class Queue extends ResourceController implements Saveable {
// allocate buildable jobs to executors // allocate buildable jobs to executors
for (BuildableItem p : new ArrayList<BuildableItem>( for (BuildableItem p : new ArrayList<BuildableItem>(
buildables)) {// copy as we'll mutate the list in the loop buildables)) {// copy as we'll mutate the list in the loop
if (p.task instanceof FlyweightTask){
continue;
}
// one last check to make sure this build is not blocked. // one last check to make sure this build is not blocked.
if (isBuildBlocked(p)) { if (isBuildBlocked(p)) {
p.leave(this); p.leave(this);
...@@ -1538,12 +1567,15 @@ public class Queue extends ResourceController implements Saveable { ...@@ -1538,12 +1567,15 @@ public class Queue extends ResourceController implements Saveable {
Label lbl = p.getAssignedLabel(); Label lbl = p.getAssignedLabel();
for (Node n : hash.list(p.task.getFullDisplayName())) { for (Node n : hash.list(p.task.getFullDisplayName())) {
final Computer c = n.toComputer(); final Computer c = n.toComputer();
if (c==null || c.isOffline()) continue; // if (c==null || c.isOffline()) continue;
if (lbl!=null && !lbl.contains(n)) continue; // if (lbl!=null && !lbl.contains(n)) continue;
if (n.canTake(p) != null) continue; if (n.canTake(p) != null) continue;
if (c==null || c.isOffline()) continue;
return new Runnable() { return new Runnable() {
@Override public void run() { @Override public void run() {
c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task)); c.startFlyWeightTask(new WorkUnitContext(p).createWorkUnit(p.task));
p.leave(Queue.this);
makePending(p); makePending(p);
} }
}; };
...@@ -2316,6 +2348,71 @@ public class Queue extends ResourceController implements Saveable { ...@@ -2316,6 +2348,71 @@ public class Queue extends ResourceController implements Saveable {
} }
} }
/**
* {@link Item} in the {@link Queue#flyWeightTasks} stage.
*/
public final class FlyWeightItem extends NotWaitingItem {
boolean buildable;
public FlyWeightItem(WaitingItem wi) {
super(wi);
}
public FlyWeightItem(NotWaitingItem ni) {
super(ni);
}
public CauseOfBlockage getCauseOfBlockage() {
ResourceActivity r = getBlockingActivity(task);
if (r != null) {
if (r == task) // blocked by itself, meaning another build is in progress
return CauseOfBlockage.fromMessage(Messages._Queue_InProgress());
return CauseOfBlockage.fromMessage(Messages._Queue_BlockedBy(r.getDisplayName()));
}
for (QueueTaskDispatcher d : QueueTaskDispatcher.all()) {
CauseOfBlockage cause = d.canRun(this);
if (cause != null)
return cause;
}
return task.getCauseOfBlockage();
}
/*package*/ void enter(Queue q) {
LOGGER.log(Level.FINE, "{0} is blocked", this);
flyWeightTasks.add(this);
buildable = true;
for (QueueListener ql : QueueListener.all()) {
try {
ql.onEnterFlyWeight(this);
} catch (Throwable e) {
// don't let this kill the queue
LOGGER.log(Level.WARNING, "QueueListener failed while processing "+this,e);
}
}
}
/*package*/ boolean leave(Queue q) {
boolean r = flyWeightTasks.remove(this);
buildable = false;
if (r) {
LOGGER.log(Level.FINE, "{0} no longer blocked", this);
for (QueueListener ql : QueueListener.all()) {
try {
ql.onLeaveFlyWeight(this);
} catch (Throwable e) {
// don't let this kill the queue
LOGGER.log(Level.WARNING, "QueueListener failed while processing "+this,e);
}
}
}
return r;
}
}
/** /**
* {@link Item} in the {@link Queue#buildables} stage. * {@link Item} in the {@link Queue#buildables} stage.
*/ */
......
...@@ -9,6 +9,7 @@ import hudson.model.Queue.BuildableItem; ...@@ -9,6 +9,7 @@ import hudson.model.Queue.BuildableItem;
import hudson.model.Queue.Item; import hudson.model.Queue.Item;
import hudson.model.Queue.LeftItem; import hudson.model.Queue.LeftItem;
import hudson.model.Queue.WaitingItem; import hudson.model.Queue.WaitingItem;
import hudson.model.Queue.FlyWeightItem;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
...@@ -29,6 +30,9 @@ import java.util.concurrent.Executor; ...@@ -29,6 +30,9 @@ import java.util.concurrent.Executor;
* @since 1.520 * @since 1.520
*/ */
public abstract class QueueListener implements ExtensionPoint { public abstract class QueueListener implements ExtensionPoint {
public void onEnterFlyWeight(FlyWeightItem fw) {}
public void onLeaveFlyWeight(FlyWeightItem fw) {}
/** /**
* When a task is submitted to the queue, it first gets to the waiting phase, * When a task is submitted to the queue, it first gets to the waiting phase,
* where it spends until the quiet period runs out and the item becomes executable. * where it spends until the quiet period runs out and the item becomes executable.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册