ParseApplication/ExternalLibs/Parse-SDK-Android/Parse/src/main/java/com/parse/TaskQueue.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();
}
}
}