/** * 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); }
/** * Checks if there are tasks ready to be run on the scheduler. Generally this is called from the * same thread that would call {@link #tick(ExceptionHandlerInterface)} (but does not have to be). * If {@link #tick(ExceptionHandlerInterface)} is not currently being called, this call indicates * if the next {@link #tick(ExceptionHandlerInterface)} will have at least one task to run. If * {@link #tick(ExceptionHandlerInterface)} is currently being invoked, this call will do a best * attempt to indicate if there is at least one more task to run (not including the task which may * currently be running). It's a best attempt as it will try not to block the thread invoking * {@link #tick(ExceptionHandlerInterface)} to prevent it from accepting additional work. * * @return {@code true} if there are task waiting to run */ public boolean hasTaskReadyToRun() { while (true) { TaskContainer nextExecuteTask = executeQueue.peek(); if (nextExecuteTask != null) { if (nextExecuteTask.running) { // loop and retry, should be removed from queue shortly Thread.yield(); } else { return true; } } else { break; } } synchronized (scheduledQueue.getModificationLock()) { Iterator<TaskContainer> it = scheduledQueue.iterator(); while (it.hasNext()) { TaskContainer scheduledTask = it.next(); if (scheduledTask.running) { continue; } else if (scheduledTask.getDelayInMillis() <= 0) { return true; } else { return false; } } } return false; }
/** * 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(); }
/** * Call to get the next task that is ready to be run. If there are no tasks, or the next task * still has a remaining delay, this will return {@code null}. * * <p>If this is being called in parallel with a {@link #tick(ExecutionHandlerInterface)} call, * the returned task may already be running. You must check the {@code TaskContainer.running} * boolean if this condition is important to you. * * @param onlyReturnReadyTask {@code false} to return scheduled tasks which may not be ready for * execution * @return next ready task, or {@code null} if there are none */ protected TaskContainer getNextTask(boolean onlyReturnReadyTask) { TaskContainer nextScheduledTask = scheduledQueue.peekFirst(); TaskContainer nextExecuteTask = executeQueue.peek(); if (nextExecuteTask != null) { if (nextScheduledTask != null) { long scheduleDelay; long executeDelay; ClockWrapper.stopForcingUpdate(); try { scheduleDelay = nextScheduledTask.getDelayInMillis(); executeDelay = nextExecuteTask.getDelayInMillis(); } finally { ClockWrapper.resumeForcingUpdate(); } if (scheduleDelay < executeDelay) { return nextScheduledTask; } else { return nextExecuteTask; } } else { return nextExecuteTask; } } else if (!onlyReturnReadyTask || (nextScheduledTask != null && nextScheduledTask.getDelayInMillis() <= 0)) { return nextScheduledTask; } else { return null; } }
@Override public boolean remove(Callable<?> task) { if (ContainerHelper.remove(executeQueue, task)) { return true; } synchronized (scheduledQueue.getModificationLock()) { return ContainerHelper.remove(scheduledQueue, task); } }