/*
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.imagepipeline.testing;
import com.facebook.common.internal.Preconditions;
/**
* A queue that executes items according to the passage of time.
*/
public class ScheduledQueue {
private final FakeClock clock;
private final DeltaQueue<Runnable> deltaQueue = new DeltaQueue<Runnable>();
private long now;
private boolean isTicking;
private long tickRemaining;
public ScheduledQueue(FakeClock clock) {
this.clock = clock;
now = clock.now();
clock.addListener(
new FakeClock.OnTickListener() {
@Override
public void onTick() {
ScheduledQueue.this.tick();
}
});
}
/**
* Runs time forwards by a given duration, executing any commands scheduled for
* execution during that time period, and any background tasks spawned by the
* scheduled tasks. Therefore, when a call to tick returns, the executor
* will be idle.
*/
private void tick() {
long newNow = clock.now();
Preconditions.checkState(!isTicking);
isTicking = true;
try {
tickRemaining = newNow - now;
now = newNow;
do {
tickRemaining = deltaQueue.tick(tickRemaining);
runUntilIdle();
} while (deltaQueue.isNotEmpty() && tickRemaining > 0);
Preconditions.checkState(isTicking);
} finally {
isTicking = false;
tickRemaining = 0;
}
}
/**
* Runs all commands scheduled to be executed immediately but does
* not tick time forward.
*/
public void runUntilIdle() {
while (!isIdle()) {
runNextPendingCommand();
}
}
/**
* Gets whether the queue is idle.
*
* @return whether there are no more items in the queue that are ready to be processed
*/
public boolean isIdle() {
return deltaQueue.isEmpty() || deltaQueue.delay() > 0;
}
/**
* Runs the next command scheduled to be executed immediately.
*/
public void runNextPendingCommand() {
Runnable runnable = deltaQueue.pop();
runnable.run();
}
/**
* Returns how much time left until next command.
*
* If there are no pending commands left will throw and exception.
*/
public long getNextPendingCommandDelay() {
if (getPendingCount() == 0) {
throw new IllegalStateException();
}
return deltaQueue.delay();
}
/**
* Adds a task to the queue that is ready to be executed.
*
* @param runnable the task to execute
*/
public void add(Runnable runnable) {
deltaQueue.add(tickRemaining, runnable);
}
/**
* Adds a task to the queue that is to be executed to after the specified delay.
*
* @param runnable the task to execute
* @param delayMs the delay before the task should be executed
*/
public void add(Runnable runnable, long delayMs) {
deltaQueue.add(tickRemaining + delayMs, runnable);
}
/**
* Removes a task from the queue
* @param runnable The runnable to remove
* @return whether the element was removed
*/
public boolean remove(Runnable runnable) {
return deltaQueue.remove(runnable);
}
/**
* Gets the count of queued runnables, regardless of whether they are ready to be run.
*
* @return the count of queued runnables
*/
public int getPendingCount() {
return deltaQueue.size();
}
}