private int handleUpdateForSinglePriorityLevel(int remainingCount,
      Iterator<Entry<Integer, TreeSet<TaskInfo>>> iterator, TaskInfo failedUpdate,
      List<TaskInfo> toUpdate, boolean newValue) {
    Entry<Integer, TreeSet<TaskInfo>> entry = iterator.next();
    TreeSet<TaskInfo> atPriority = entry.getValue();
    WM_LOG.info("At priority " + entry.getKey() + " observing " + entry.getValue().size());

    Iterator<TaskInfo> atPriorityIter = newValue ? atPriority.iterator() : atPriority.descendingIterator();
    TreeMap<Integer, TreeSet<TaskInfo>> toMap = newValue ? guaranteedTasks : speculativeTasks,
        fromMap = newValue ? speculativeTasks : guaranteedTasks;
    while (atPriorityIter.hasNext() && remainingCount > 0) {
      TaskInfo taskInfo = atPriorityIter.next();
      if (taskInfo == failedUpdate) continue;
      atPriorityIter.remove();
      synchronized (taskInfo) {
        assert taskInfo.isGuaranteed != newValue;
        taskInfo.isGuaranteed = newValue;
        // When we introduce a discrepancy to the state we give the task to an updater unless it
        // was already given to one.  If the updater is already doing stuff, it would handle the
        // changed state when it's done with whatever it's doing. The updater is not going to
        // give up until the discrepancies are eliminated.
        if (!taskInfo.isPendingUpdate) {
          setUpdateStartedUnderTiLock(taskInfo);
          WM_LOG.info("Adding " + taskInfo.attemptId + " to update");
          toUpdate.add(taskInfo);
        } else {
          WM_LOG.info("Not adding " + taskInfo.attemptId + " to update - already pending");
        }
      }
      addToRunningTasksMap(toMap, taskInfo);
      --remainingCount;
    }
    // Remove entire priority level if it's been emptied.
    // We do this before checking failedUpdate because that might break the iterator.
    if (atPriority.isEmpty()) {
      iterator.remove();
    }
    // We include failedUpdate only after looking at all the tasks at the same priority.
    if (failedUpdate != null && entry.getKey() == failedUpdate.priority.getPriority()
        && remainingCount > 0) {
      // This will break the iterator. However, this is the last task we can add the way this currently
      // runs (only one duck is distributed when failedUpdate is present), so that should be ok.
      removeFromRunningTaskMap(fromMap, failedUpdate.task, failedUpdate);
      synchronized (failedUpdate) {
        assert failedUpdate.isGuaranteed != newValue;
        failedUpdate.isGuaranteed = newValue;
        setUpdateStartedUnderTiLock(failedUpdate);
      }
      WM_LOG.info("Adding failed " + failedUpdate.attemptId + " to update");
      // Do not check the state - this is coming from the updater under epic lock.
      toUpdate.add(failedUpdate);
      addToRunningTasksMap(toMap, failedUpdate);
      --remainingCount;
    }

    return remainingCount;
  }
Go through the running tasks Tree in descending priority and find a candidate to preempt. The candidate should be:
1. lower priority than forTask
2. below forTask in the DAG
3. not considered already for preemption
A successful candidate is added to the preemptionCandidates queue for preemption by the scheduling loop.
@param runningTasks Tree of running tasks
@param forTask Task to find preemption candidate for
@param preemptHosts Hosts preference
@return true when preemption candidate is found
llap-tez/src/java/org/apache/hadoop/hive/llap/tezplugins/LlapTaskSchedulerService.java