119 lines
3.5 KiB
Java
119 lines
3.5 KiB
Java
/*
|
|
* Copyright (c) 2015-present, Parse, LLC.
|
|
* 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.parse;
|
|
|
|
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<Void> 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<Void> getTaskToAwait() {
|
|
lock.lock();
|
|
try {
|
|
Task<Void> toAwait = tail != null ? tail : Task.<Void> forResult(null);
|
|
return toAwait.continueWith(new Continuation<Void, Void>() {
|
|
@Override
|
|
public Void then(Task<Void> 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.
|
|
*/
|
|
<T> Task<T> enqueue(Continuation<Void, Task<T>> taskStart) {
|
|
lock.lock();
|
|
try {
|
|
Task<T> task;
|
|
Task<Void> oldTail = tail != null ? tail : Task.<Void> 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<Void> 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 <T> Continuation<T, Task<T>> waitFor(final Task<Void> toAwait) {
|
|
return new Continuation<T, Task<T>>() {
|
|
@Override
|
|
public Task<T> then(final Task<T> task) throws Exception {
|
|
return toAwait.continueWithTask(new Continuation<Void, Task<T>>() {
|
|
@Override
|
|
public Task<T> then(Task<Void> 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();
|
|
}
|
|
}
|
|
}
|