use of hudson.model.queue.MappingWorksheet.Mapping in project hudson-2.x by hudson.
the class Queue method pop.
/**
* Called by the executor to fetch something to build next.
* <p>
* This method blocks until a next project becomes buildable.
*/
public synchronized WorkUnit pop() throws InterruptedException {
final Executor exec = Executor.currentExecutor();
if (exec instanceof OneOffExecutor) {
OneOffExecutor ooe = (OneOffExecutor) exec;
final WorkUnit wu = ooe.getWorkUnit();
pendings.remove(wu.context.item);
return wu;
}
try {
while (true) {
final JobOffer offer = new JobOffer(exec);
long sleep = -1;
// consider myself parked
assert !parked.containsKey(exec);
parked.put(exec, offer);
// reuse executor thread to do a queue maintenance.
// at the end of this we get all the buildable jobs
// in the buildables field.
maintain();
// allocate buildable jobs to executors
Iterator<BuildableItem> itr = buildables.iterator();
while (itr.hasNext()) {
BuildableItem p = itr.next();
// one last check to make sure this build is not blocked.
if (isBuildBlocked(p.task)) {
itr.remove();
blockedProjects.put(p.task, new BlockedItem(p));
continue;
}
List<JobOffer> candidates = new ArrayList<JobOffer>(parked.size());
for (JobOffer j : parked.values()) if (j.canTake(p.task))
candidates.add(j);
MappingWorksheet ws = new MappingWorksheet(p, candidates);
Mapping m = loadBalancer.map(p.task, ws);
if (m == null)
// check if we can execute other projects
continue;
// found a matching executor. use it.
WorkUnitContext wuc = new WorkUnitContext(p);
m.execute(wuc);
itr.remove();
if (!wuc.getWorkUnits().isEmpty())
pendings.add(p);
}
if (!waitingList.isEmpty()) {
// wait until the first item in the queue is due
sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis();
// avoid wait(0)
if (sleep < 100)
sleep = 100;
}
if (sleep == -1)
offer.event.block();
else
offer.event.block(sleep);
// retract the offer object
assert parked.get(exec) == offer;
parked.remove(exec);
// am I woken up because I have a project to build?
if (offer.workUnit != null) {
// if so, just build it
LOGGER.fine("Pop returning " + offer.workUnit + " for " + exec.getName());
// TODO: I think this has to be done by the last executor that leaves the pop(), not by main executor
if (offer.workUnit.isMainWork())
pendings.remove(offer.workUnit.context.item);
return offer.workUnit;
}
// otherwise run a queue maintenance
}
} finally {
// remove myself from the parked list
JobOffer offer = parked.remove(exec);
if (offer != null && offer.workUnit != null) {
// we are already assigned a project, but now we can't handle it.
offer.workUnit.context.abort(new AbortException());
}
// since this executor might have been chosen for
// maintenance, schedule another one. Worst case
// we'll just run a pointless maintenance, and that's
// fine.
scheduleMaintenance();
}
}
use of hudson.model.queue.MappingWorksheet.Mapping in project hudson-2.x by hudson.
the class BackFiller method makeTentativePlan.
private TentativePlan makeTentativePlan(BuildableItem bi) {
if (recursion)
return null;
recursion = true;
try {
// pretend for now that all executors are available and decide some assignment that's executable.
List<PseudoExecutorSlot> slots = new ArrayList<PseudoExecutorSlot>();
for (Computer c : Hudson.getInstance().getComputers()) {
if (c.isOffline())
continue;
for (Executor e : c.getExecutors()) {
slots.add(new PseudoExecutorSlot(e));
}
}
// also ignore all load predictions as we just want to figure out some executable assignment
// and we are not trying to figure out if this task is executable right now.
MappingWorksheet worksheet = new MappingWorksheet(bi, slots, Collections.<LoadPredictor>emptyList());
Mapping m = Hudson.getInstance().getQueue().getLoadBalancer().map(bi.task, worksheet);
if (m == null)
return null;
// figure out how many executors we need on each computer?
Map<Computer, Integer> footprint = new HashMap<Computer, Integer>();
for (Entry<WorkChunk, ExecutorChunk> e : m.toMap().entrySet()) {
Computer c = e.getValue().computer;
Integer v = footprint.get(c);
if (v == null)
v = 0;
v += e.getKey().size();
footprint.put(c, v);
}
// the point of a tentative plan is to displace other jobs to create a point in time
// where this task can start executing. An incorrectly estimated duration is not
// a problem in this regard, as we just need enough idle executors in the right moment.
// The downside of guessing the duration wrong is that we can end up creating tentative plans
// afterward that may be incorrect, but those plans will be rebuilt.
long d = bi.task.getEstimatedDuration();
if (d <= 0)
d = TimeUnit2.MINUTES.toMillis(5);
TimeRange slot = new TimeRange(System.currentTimeMillis(), d);
// start executing this guy.
for (Entry<Computer, Integer> e : footprint.entrySet()) {
Computer computer = e.getKey();
Timeline timeline = new Timeline();
for (LoadPredictor lp : LoadPredictor.all()) {
for (FutureLoad fl : Iterables.limit(lp.predict(worksheet, computer, slot.start, slot.end), 100)) {
timeline.insert(fl.startTime, fl.startTime + fl.duration, fl.numExecutors);
}
}
Long x = timeline.fit(slot.start, slot.duration, computer.countExecutors() - e.getValue());
// if no suitable range was found in [slot.start,slot.end), slot.end would be a good approximation
if (x == null)
x = slot.end;
slot = slot.shiftTo(x);
}
TentativePlan tp = new TentativePlan(footprint, slot);
bi.addAction(tp);
return tp;
} finally {
recursion = false;
}
}
Aggregations