private boolean handleScheduleAttemptedRejection(TaskWrapper rejected) {
    // TODO: is this check even needed given what the caller checks?
    if (!enablePreemption || preemptionQueue.isEmpty()) {
      return false;
    }
    LOG.debug("Preemption Queue: {}", preemptionQueue);

    // This call checks under lock if we can actually preempt the task.
    // It is possible to have a race where the update (that's also under lock) makes the
    // task finishable or guaranteed between the remove and kill, but it's the same timing
    // issue as would happen is there was a tiny delay on the network, so we don't care.
    TaskWrapper victim = getSuitableVictimFromPreemptionQueue(rejected);
    if (victim == null) {
      return false; // Woe us.
    }
    LOG.info("Invoking kill task for {} due to pre-emption to run {}", victim.getRequestId(), rejected.getRequestId());
    // The task will either be killed or is already in the process of completing, which will
    // trigger the next scheduling run, or result in available slots being higher than 0,
    // which will cause the scheduler loop to continue.
    victim.getTaskRunnerCallable().killTask();
    // We've killed something and may want to wait for it to die.
    return true;
  }
Assumes the epic lock is already taken.
llap-server/src/java/org/apache/hadoop/hive/llap/daemon/impl/TaskExecutorService.java