/** * Called by the executor to fetch something to build next. * * <p>This method blocks until a next project becomes buildable. */ public Queue.Item pop() throws InterruptedException { final Executor exec = Executor.currentExecutor(); try { while (true) { final JobOffer offer = new JobOffer(exec); long sleep = -1; synchronized (this) { // 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; } JobOffer runner = loadBalancer.choose(p.task, new ApplicableJobOfferList(p.task)); if (runner == null) // if we couldn't find the executor that fits, // just leave it in the buildables list and // check if we can execute other projects continue; assert runner.canTake(p.task); // found a matching executor. use it. runner.set(p); itr.remove(); } // we went over all the buildable projects and awaken // all the executors that got work to do. now, go to sleep // until this thread is awakened. If this executor assigned a job to // itself above, the block method will return immediately. if (!waitingList.isEmpty()) { // wait until the first item in the queue is due sleep = peek().timestamp.getTimeInMillis() - new GregorianCalendar().getTimeInMillis(); if (sleep < 100) sleep = 100; // avoid wait(0) } } // this needs to be done outside synchronized block, // so that executors can maintain a queue while others are sleeping if (sleep == -1) offer.event.block(); else offer.event.block(sleep); synchronized (this) { // 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.item != null) { // if so, just build it LOGGER.fine("Pop returning " + offer.item + " for " + exec.getName()); offer.item.future.startExecuting(exec); return offer.item; } // otherwise run a queue maintenance } } } finally { synchronized (this) { // remove myself from the parked list JobOffer offer = parked.remove(exec); if (offer != null && offer.item != null) { // we are already assigned a project, // ask for someone else to build it. // note that while this thread is waiting for CPU // someone else can schedule this build again, // so check the contains method first. if (!contains(offer.item.task)) buildables.put(offer.item.task, offer.item); } // 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(); } } }
protected void doRun() { Queue q = queue.get(); if (q != null) q.maintain(); else cancel(); }