/** Wipes out all the items currently in the queue, as if all of them are cancelled at once. */ public synchronized void clear() { for (WaitingItem i : waitingList) i.onCancelled(); waitingList.clear(); blockedProjects.cancelAll(); buildables.cancelAll(); scheduleMaintenance(); }
/** * 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(); } } }
/** * Schedules an execution of a task. * * @since 1.311 * @return null if this task is already in the queue and therefore the add operation was no-op. * Otherwise indicates the {@link WaitingItem} object added, although the nature of the queue * is that such {@link Item} only captures the state of the item at a particular moment, and * by the time you inspect the object, some of its information can be already stale. * <p>That said, one can still look at {@link WaitingItem#future}, {@link WaitingItem#id}, * etc. */ private synchronized WaitingItem scheduleInternal(Task p, int quietPeriod, List<Action> actions) { WaitingItem added = null; List<Item> items = getItems(p); Calendar due = new GregorianCalendar(); due.add(Calendar.SECOND, quietPeriod); List<Item> duplicatesInQueue = new ArrayList<Item>(); for (Item item : items) { boolean shouldScheduleItem = false; for (Action action : item.getActions()) { if (action instanceof QueueAction) shouldScheduleItem |= ((QueueAction) action).shouldSchedule(actions); } for (Action action : actions) { if (action instanceof QueueAction) { shouldScheduleItem |= ((QueueAction) action).shouldSchedule(item.getActions()); } } if (!shouldScheduleItem) { duplicatesInQueue.add(item); } } if (duplicatesInQueue.size() == 0) { LOGGER.fine(p.getFullDisplayName() + " added to queue"); // put the item in the queue waitingList.add(added = new WaitingItem(due, p, actions)); } else { // the requested build is already queued, so will not be added List<WaitingItem> waitingDuplicates = new ArrayList<WaitingItem>(); for (Item item : duplicatesInQueue) { for (Action a : actions) { if (a instanceof FoldableAction) { ((FoldableAction) a).foldIntoExisting(item.task, item.getActions()); } } if ((item instanceof WaitingItem)) waitingDuplicates.add((WaitingItem) item); } if (duplicatesInQueue.size() == 0) { // all duplicates in the queue are already in the blocked or // buildable stage no need to requeue return null; } // TODO: avoid calling scheduleMaintenance() if none of the waiting items // actually change for (WaitingItem wi : waitingDuplicates) { if (quietPeriod <= 0) { // the user really wants to build now, and they mean NOW. // so let's pull in the timestamp if we can. if (wi.timestamp.before(due)) continue; } else { // otherwise we do the normal quiet period implementation if (wi.timestamp.after(due)) continue; // quiet period timer reset. start the period over again } // waitingList is sorted, so when we change a timestamp we need to maintain order waitingList.remove(wi); wi.timestamp = due; waitingList.add(wi); } } scheduleMaintenance(); // let an executor know that a new item is in the queue. return added; }