/** * 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 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(); }
/** * Abstract call to get the value the scheduler should use to represent the current time. This can * be overridden if someone wanted to artificially change the time. * * @return current time in milliseconds */ protected long nowInMillis() { return ClockWrapper.getSemiAccurateMillis(); }