co.easimart.TaskQueue Maven / Gradle / Ivy
package co.easimart;
import java.util.Arrays;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import bolts.Continuation;
import bolts.Task;
/**
* A helper class for enqueueing tasks
*/
/** package */ class TaskQueue {
/**
* We only need to keep the tail of the queue. Cancelled tasks will just complete
* normally/immediately when their turn arrives.
*/
private Task tail;
private final Lock lock = new ReentrantLock();
/**
* Gets a task that can be safely awaited and is dependent on the current tail of the queue. This
* essentially gives us a proxy for the tail end of the queue that can be safely cancelled.
*
* @return A new task that should be awaited by enqueued tasks.
*/
private Task getTaskToAwait() {
lock.lock();
try {
Task toAwait = tail != null ? tail : Task. forResult(null);
return toAwait.continueWith(new Continuation() {
@Override
public Void then(Task task) throws Exception {
return null;
}
});
} finally {
lock.unlock();
}
}
/**
* Enqueues a task created by taskStart.
*
* @param taskStart
* A function given a task to await once state is snapshotted (e.g. after capturing
* session tokens at the time of the save call). Awaiting this task will wait for the
* created task's turn in the queue.
* @return The task created by the taskStart function.
*/
Task enqueue(Continuation> taskStart) {
lock.lock();
try {
Task task;
Task oldTail = tail != null ? tail : Task. forResult(null);
// The task created by taskStart is responsible for waiting for the task passed into it before
// doing its work (this gives it an opportunity to do startup work or save state before
// waiting for its turn in the queue)
try {
Task toAwait = getTaskToAwait();
task = taskStart.then(toAwait);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
// The tail task should be dependent on the old tail as well as the newly-created task. This
// prevents cancellation of the new task from causing the queue to run out of order.
tail = Task.whenAll(Arrays.asList(oldTail, task));
return task;
} finally {
lock.unlock();
}
}
/**
* Creates a continuation that will wait for the given task to complete before running the next
* continuations.
*/
static Continuation> waitFor(final Task toAwait) {
return new Continuation>() {
@Override
public Task then(final Task task) throws Exception {
return toAwait.continueWithTask(new Continuation>() {
@Override
public Task then(Task ignored) throws Exception {
return task;
}
});
}
};
}
Lock getLock() {
return lock;
}
void waitUntilFinished() throws InterruptedException {
lock.lock();
try {
if (tail == null) {
return;
}
tail.waitForCompletion();
} finally {
lock.unlock();
}
}
}