/**
   * Removes any tasks waiting to be run. Will not interrupt any tasks currently running if {@link
   * #tick(ExceptionHandlerInterface)} is being called. But will avoid additional tasks from being
   * run on the current {@link #tick(ExceptionHandlerInterface)} call.
   *
   * <p>If tasks are added concurrently during this invocation they may or may not be removed.
   *
   * @return List of runnables which were waiting in the task queue to be executed (and were now
   *     removed)
   */
  public List<Runnable> clearTasks() {
    List<TaskContainer> containers;
    synchronized (scheduledQueue.getModificationLock()) {
      containers = new ArrayList<TaskContainer>(executeQueue.size() + scheduledQueue.size());

      Iterator<? extends TaskContainer> it = executeQueue.iterator();
      while (it.hasNext()) {
        TaskContainer tc = it.next();
        /* we must use executeQueue.remove(Object) instead of it.remove()
         * This is to assure it is atomically removed (without executing)
         */
        if (!tc.running && executeQueue.remove(tc)) {
          int index = ListUtils.getInsertionEndIndex(containers, tc, true);
          containers.add(index, tc);
        }
      }

      it = scheduledQueue.iterator();
      while (it.hasNext()) {
        TaskContainer tc = it.next();
        if (!tc.running) {
          int index = ListUtils.getInsertionEndIndex(containers, tc, true);
          containers.add(index, tc);
        }
      }
      scheduledQueue.clear();
    }

    return ContainerHelper.getContainedRunnables(containers);
  }
    @Override
    public void runComplete() {
      synchronized (scheduledQueue.getModificationLock()) {
        ClockWrapper.stopForcingUpdate();
        try {
          updateNextRunTime();

          // almost certainly will be the first item in the queue
          int currentIndex = scheduledQueue.indexOf(this);
          if (currentIndex < 0) {
            // task was removed from queue, do not re-insert
            return;
          }
          long nextDelay = getDelayInMillis();
          if (nextDelay < 0) {
            nextDelay = 0;
          }
          int insertionIndex = ListUtils.getInsertionEndIndex(scheduledQueue, nextDelay, true);

          scheduledQueue.reposition(currentIndex, insertionIndex);
        } finally {
          ClockWrapper.resumeForcingUpdate();
        }
      }
    }
  /**
   * Adds a task to scheduled/recurring queue. This call is more expensive than {@link
   * #addImmediateExecute(OneTimeTask)}, but is necessary for any tasks which are either delayed or
   * recurring.
   *
   * @param runnable Task to execute on next {@link #tick(ExceptionHandlerInterface)} call
   */
  protected void addScheduled(TaskContainer runnable) {
    synchronized (scheduledQueue.getModificationLock()) {
      ClockWrapper.stopForcingUpdate();
      try {
        int insertionIndex = ListUtils.getInsertionEndIndex(scheduledQueue, runnable, true);

        scheduledQueue.add(insertionIndex, runnable);
      } finally {
        ClockWrapper.resumeForcingUpdate();
      }
    }

    notifyQueueUpdate();
  }