/*
* Copyright (c) 2014, 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 bolts;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Represents the result of an asynchronous operation.
*
* @param <TResult>
* The type of the result of the task.
*/
public class Task<TResult> {
/**
* An {@link java.util.concurrent.Executor} that executes tasks in parallel.
*/
public static final ExecutorService BACKGROUND_EXECUTOR = BoltsExecutors.background();
/**
* An {@link java.util.concurrent.Executor} that executes tasks in the current thread unless
* the stack runs too deep, at which point it will delegate to {@link Task#BACKGROUND_EXECUTOR} in
* order to trim the stack.
*/
private static final Executor IMMEDIATE_EXECUTOR = BoltsExecutors.immediate();
/**
* An {@link java.util.concurrent.Executor} that executes tasks on the UI thread.
*/
public static final Executor UI_THREAD_EXECUTOR = AndroidExecutors.uiThread();
private final Object lock = new Object();
private boolean complete;
private boolean cancelled;
private TResult result;
private Exception error;
private List<Continuation<TResult, Void>> continuations;
private Task() {
continuations = new ArrayList<Continuation<TResult, Void>>();
}
/**
* Creates a TaskCompletionSource that orchestrates a Task. This allows the creator of a task to
* be solely responsible for its completion.
*
* @return A new TaskCompletionSource.
*/
public static <TResult> Task<TResult>.TaskCompletionSource create() {
Task<TResult> task = new Task<TResult>();
return task.new TaskCompletionSource();
}
/**
* @return {@code true} if the task completed (has a result, an error, or was cancelled.
* {@code false} otherwise.
*/
public boolean isCompleted() {
synchronized (lock) {
return complete;
}
}
/**
* @return {@code true} if the task was cancelled, {@code false} otherwise.
*/
public boolean isCancelled() {
synchronized (lock) {
return cancelled;
}
}
/**
* @return {@code true} if the task has an error, {@code false} otherwise.
*/
public boolean isFaulted() {
synchronized (lock) {
return error != null;
}
}
/**
* @return The result of the task, if set. {@code null} otherwise.
*/
public TResult getResult() {
synchronized (lock) {
return result;
}
}
/**
* @return The error for the task, if set. {@code null} otherwise.
*/
public Exception getError() {
synchronized (lock) {
return error;
}
}
/**
* Blocks until the task is complete.
*/
public void waitForCompletion() throws InterruptedException {
synchronized (lock) {
if (!isCompleted()) {
lock.wait();
}
}
}
/**
* Creates a completed task with the given value.
*/
public static <TResult> Task<TResult> forResult(TResult value) {
Task<TResult>.TaskCompletionSource tcs = Task.create();
tcs.setResult(value);
return tcs.getTask();
}
/**
* Creates a faulted task with the given error.
*/
public static <TResult> Task<TResult> forError(Exception error) {
Task<TResult>.TaskCompletionSource tcs = Task.create();
tcs.setError(error);
return tcs.getTask();
}
/**
* Creates a cancelled task.
*/
public static <TResult> Task<TResult> cancelled() {
Task<TResult>.TaskCompletionSource tcs = Task.create();
tcs.setCancelled();
return tcs.getTask();
}
/**
* Creates a task that completes after a time delay.
*
* @param delay The number of milliseconds to wait before completing the returned task. Zero and
* negative values are treated as requests for immediate execution.
*/
public static Task<Void> delay(long delay) {
return delay(delay, BoltsExecutors.scheduled());
}
/* package */ static Task<Void> delay(long delay, ScheduledExecutorService executor) {
if (delay <= 0) {
return Task.forResult(null);
}
final Task<Void>.TaskCompletionSource tcs = Task.create();
executor.schedule(new Runnable() {
@Override
public void run() {
tcs.setResult(null);
}
}, delay, TimeUnit.MILLISECONDS);
return tcs.getTask();
}
/**
* Makes a fluent cast of a Task's result possible, avoiding an extra continuation just to cast
* the type of the result.
*/
public <TOut> Task<TOut> cast() {
@SuppressWarnings("unchecked")
Task<TOut> task = (Task<TOut>) this;
return task;
}
/**
* Turns a Task<T> into a Task<Void>, dropping any result.
*/
public Task<Void> makeVoid() {
return this.continueWithTask(new Continuation<TResult, Task<Void>>() {
@Override
public Task<Void> then(Task<TResult> task) throws Exception {
if (task.isCancelled()) {
return Task.cancelled();
}
if (task.isFaulted()) {
return Task.forError(task.getError());
}
return Task.forResult(null);
}
});
}
/**
* Invokes the callable on a background thread, returning a Task to represent the operation.
*
* If you want to cancel the resulting Task throw a {@link java.util.concurrent.CancellationException}
* from the callable.
*/
public static <TResult> Task<TResult> callInBackground(Callable<TResult> callable) {
return call(callable, BACKGROUND_EXECUTOR, null);
}
/**
* Invokes the callable on a background thread, returning a Task to represent the operation.
*/
public static <TResult> Task<TResult> callInBackground(Callable<TResult> callable, CancellationToken ct) {
return call(callable, BACKGROUND_EXECUTOR, ct);
}
/**
* Invokes the callable using the given executor, returning a Task to represent the operation.
*
* If you want to cancel the resulting Task throw a {@link java.util.concurrent.CancellationException}
* from the callable.
*/
public static <TResult> Task<TResult> call(final Callable<TResult> callable, Executor executor) {
return call(callable, executor, null);
}
/**
* Invokes the callable using the given executor, returning a Task to represent the operation.
*/
public static <TResult> Task<TResult> call(final Callable<TResult> callable, Executor executor,
final CancellationToken ct) {
final Task<TResult>.TaskCompletionSource tcs = Task.create();
executor.execute(new Runnable() {
@Override
public void run() {
if (ct != null && ct.isCancellationRequested()) {
tcs.setCancelled();
return;
}
try {
tcs.setResult(callable.call());
} catch (CancellationException e) {
tcs.setCancelled();
} catch (Exception e) {
tcs.setError(e);
}
}
});
return tcs.getTask();
}
/**
* Invokes the callable on the current thread, producing a Task.
*
* If you want to cancel the resulting Task throw a {@link java.util.concurrent.CancellationException}
* from the callable.
*/
public static <TResult> Task<TResult> call(final Callable<TResult> callable) {
return call(callable, IMMEDIATE_EXECUTOR, null);
}
/**
* Invokes the callable on the current thread, producing a Task.
*/
public static <TResult> Task<TResult> call(final Callable<TResult> callable, CancellationToken ct) {
return call(callable, IMMEDIATE_EXECUTOR, ct);
}
/**
* Creates a task that will complete when any of the supplied tasks have completed.
* <p/>
* The returned task will complete when any of the supplied tasks has completed. The returned task
* will always end in the completed state with its result set to the first task to complete. This
* is true even if the first task to complete ended in the canceled or faulted state.
*
* @param tasks
* The tasks to wait on for completion.
* @return A task that represents the completion of one of the supplied tasks.
* The return task's result is the task that completed.
*/
public static <TResult> Task<Task<TResult>> whenAnyResult(Collection<? extends Task<TResult>> tasks) {
if (tasks.size() == 0) {
return Task.forResult(null);
}
final Task<Task<TResult>>.TaskCompletionSource firstCompleted = Task.create();
final AtomicBoolean isAnyTaskComplete = new AtomicBoolean(false);
for (Task<TResult> task : tasks) {
task.continueWith(new Continuation<TResult, Void>() {
@Override
public Void then(Task<TResult> task) {
if (isAnyTaskComplete.compareAndSet(false, true)) {
firstCompleted.setResult(task);
}
return null;
}
});
}
return firstCompleted.getTask();
}
/**
* Creates a task that will complete when any of the supplied tasks have completed.
* <p/>
* The returned task will complete when any of the supplied tasks has completed. The returned task
* will always end in the completed state with its result set to the first task to complete. This
* is true even if the first task to complete ended in the canceled or faulted state.
*
* @param tasks
* The tasks to wait on for completion.
* @return A task that represents the completion of one of the supplied tasks.
* The return task's Result is the task that completed.
*/
@SuppressWarnings("unchecked")
public static Task<Task<?>> whenAny(Collection<? extends Task<?>> tasks) {
if (tasks.size() == 0) {
return Task.forResult(null);
}
final Task<Task<?>>.TaskCompletionSource firstCompleted = Task.create();
final AtomicBoolean isAnyTaskComplete = new AtomicBoolean(false);
for (Task<?> task : tasks) {
((Task<Object>) task).continueWith(new Continuation<Object, Void>() {
@Override
public Void then(Task<Object> task) {
if (isAnyTaskComplete.compareAndSet(false, true)) {
firstCompleted.setResult(task);
}
return null;
}
});
}
return firstCompleted.getTask();
}
/**
* Creates a task that completes when all of the provided tasks are complete.
* <p/>
* If any of the supplied tasks completes in a faulted state, the returned task will also complete
* in a faulted state, where its exception will resolve to that {@link java.lang.Exception} if a
* single task fails or an {@link AggregateException} of all the {@link java.lang.Exception}s
* if multiple tasks fail.
* <p/>
* If none of the supplied tasks faulted but at least one of them was cancelled, the returned
* task will end as cancelled.
* <p/>
* If none of the tasks faulted and none of the tasks were cancelled, the resulting task will end
* completed. The result of the returned task will be set to a list containing all of the results
* of the supplied tasks in the same order as they were provided (e.g. if the input tasks collection
* contained t1, t2, t3, the output task's result will return an {@code List<TResult>}
* where {@code list.get(0) == t1.getResult(), list.get(1) == t2.getResult(), and
* list.get(2) == t3.getResult()}).
* <p/>
* If the supplied collection contains no tasks, the returned task will immediately transition to
* a completed state before it's returned to the caller.
* The returned {@code List<TResult>} will contain of 0 elements.
*
* @param tasks The tasks that the return value will wait for before completing.
* @return A Task that will resolve to {@code List<TResult>} when all the tasks are resolved.
*/
public static <TResult> Task<List<TResult>> whenAllResult(final Collection<? extends Task<TResult>> tasks) {
return whenAll(tasks).onSuccess(new Continuation<Void, List<TResult>>() {
@Override
public List<TResult> then(Task<Void> task) throws Exception {
if (tasks.size() == 0) {
return Collections.emptyList();
}
List<TResult> results = new ArrayList<>();
for (Task<TResult> individualTask : tasks) {
results.add(individualTask.getResult());
}
return results;
}
});
}
/**
* Creates a task that completes when all of the provided tasks are complete.
* <p/>
* If any of the supplied tasks completes in a faulted state, the returned task will also complete
* in a faulted state, where its exception will resolve to that {@link java.lang.Exception} if a
* single task fails or an {@link AggregateException} of all the {@link java.lang.Exception}s
* if multiple tasks fail.
* <p/>
* If none of the supplied tasks faulted but at least one of them was cancelled, the returned
* task will end as cancelled.
* <p/>
* If none of the tasks faulted and none of the tasks were canceled, the resulting task will
* end in the completed state.
* <p/>
* If the supplied collection contains no tasks, the returned task will immediately transition
* to a completed state before it's returned to the caller.
*
* @param tasks The tasks that the return value will wait for before completing.
* @return A Task that will resolve to {@code Void} when all the tasks are resolved.
*/
public static Task<Void> whenAll(Collection<? extends Task<?>> tasks) {
if (tasks.size() == 0) {
return Task.forResult(null);
}
final Task<Void>.TaskCompletionSource allFinished = Task.create();
final ArrayList<Exception> causes = new ArrayList<Exception>();
final Object errorLock = new Object();
final AtomicInteger count = new AtomicInteger(tasks.size());
final AtomicBoolean isCancelled = new AtomicBoolean(false);
for (Task<?> task : tasks) {
@SuppressWarnings("unchecked")
Task<Object> t = (Task<Object>) task;
t.continueWith(new Continuation<Object, Void>() {
@Override
public Void then(Task<Object> task) {
if (task.isFaulted()) {
synchronized (errorLock) {
causes.add(task.getError());
}
}
if (task.isCancelled()) {
isCancelled.set(true);
}
if (count.decrementAndGet() == 0) {
if (causes.size() != 0) {
if (causes.size() == 1) {
allFinished.setError(causes.get(0));
} else {
Exception error = new AggregateException(
String.format("There were %d exceptions.", causes.size()),
causes);
allFinished.setError(error);
}
} else if (isCancelled.get()) {
allFinished.setCancelled();
} else {
allFinished.setResult(null);
}
}
return null;
}
});
}
return allFinished.getTask();
}
/**
* Continues a task with the equivalent of a Task-based while loop, where the body of the loop is
* a task continuation.
*/
public Task<Void> continueWhile(Callable<Boolean> predicate,
Continuation<Void, Task<Void>> continuation) {
return continueWhile(predicate, continuation, IMMEDIATE_EXECUTOR, null);
}
/**
* Continues a task with the equivalent of a Task-based while loop, where the body of the loop is
* a task continuation.
*/
public Task<Void> continueWhile(Callable<Boolean> predicate,
Continuation<Void, Task<Void>> continuation, CancellationToken ct) {
return continueWhile(predicate, continuation, IMMEDIATE_EXECUTOR, ct);
}
/**
* Continues a task with the equivalent of a Task-based while loop, where the body of the loop is
* a task continuation.
*/
public Task<Void> continueWhile(final Callable<Boolean> predicate,
final Continuation<Void, Task<Void>> continuation, final Executor executor) {
return continueWhile(predicate, continuation, executor, null);
}
/**
* Continues a task with the equivalent of a Task-based while loop, where the body of the loop is
* a task continuation.
*/
public Task<Void> continueWhile(final Callable<Boolean> predicate,
final Continuation<Void, Task<Void>> continuation, final Executor executor,
final CancellationToken ct) {
final Capture<Continuation<Void, Task<Void>>> predicateContinuation =
new Capture<Continuation<Void, Task<Void>>>();
predicateContinuation.set(new Continuation<Void, Task<Void>>() {
@Override
public Task<Void> then(Task<Void> task) throws Exception {
if (ct != null && ct.isCancellationRequested()) {
return Task.cancelled();
}
if (predicate.call()) {
return Task.<Void> forResult(null).onSuccessTask(continuation, executor)
.onSuccessTask(predicateContinuation.get(), executor);
}
return Task.forResult(null);
}
});
return makeVoid().continueWithTask(predicateContinuation.get(), executor);
}
/**
* Adds a continuation that will be scheduled using the executor, returning a new task that
* completes after the continuation has finished running. This allows the continuation to be
* scheduled on different thread.
*/
public <TContinuationResult> Task<TContinuationResult> continueWith(
final Continuation<TResult, TContinuationResult> continuation, final Executor executor) {
return continueWith(continuation, executor, null);
}
/**
* Adds a continuation that will be scheduled using the executor, returning a new task that
* completes after the continuation has finished running. This allows the continuation to be
* scheduled on different thread.
*/
public <TContinuationResult> Task<TContinuationResult> continueWith(
final Continuation<TResult, TContinuationResult> continuation, final Executor executor,
final CancellationToken ct) {
boolean completed;
final Task<TContinuationResult>.TaskCompletionSource tcs = Task.create();
synchronized (lock) {
completed = this.isCompleted();
if (!completed) {
this.continuations.add(new Continuation<TResult, Void>() {
@Override
public Void then(Task<TResult> task) {
completeImmediately(tcs, continuation, task, executor, ct);
return null;
}
});
}
}
if (completed) {
completeImmediately(tcs, continuation, this, executor, ct);
}
return tcs.getTask();
}
/**
* Adds a synchronous continuation to this task, returning a new task that completes after the
* continuation has finished running.
*/
public <TContinuationResult> Task<TContinuationResult> continueWith(
Continuation<TResult, TContinuationResult> continuation) {
return continueWith(continuation, IMMEDIATE_EXECUTOR, null);
}
/**
* Adds a synchronous continuation to this task, returning a new task that completes after the
* continuation has finished running.
*/
public <TContinuationResult> Task<TContinuationResult> continueWith(
Continuation<TResult, TContinuationResult> continuation, CancellationToken ct) {
return continueWith(continuation, IMMEDIATE_EXECUTOR, ct);
}
/**
* Adds an Task-based continuation to this task that will be scheduled using the executor,
* returning a new task that completes after the task returned by the continuation has completed.
*/
public <TContinuationResult> Task<TContinuationResult> continueWithTask(
final Continuation<TResult, Task<TContinuationResult>> continuation, final Executor executor) {
return continueWithTask(continuation, executor, null);
}
/**
* Adds an Task-based continuation to this task that will be scheduled using the executor,
* returning a new task that completes after the task returned by the continuation has completed.
*/
public <TContinuationResult> Task<TContinuationResult> continueWithTask(
final Continuation<TResult, Task<TContinuationResult>> continuation, final Executor executor,
final CancellationToken ct) {
boolean completed;
final Task<TContinuationResult>.TaskCompletionSource tcs = Task.create();
synchronized (lock) {
completed = this.isCompleted();
if (!completed) {
this.continuations.add(new Continuation<TResult, Void>() {
@Override
public Void then(Task<TResult> task) {
completeAfterTask(tcs, continuation, task, executor, ct);
return null;
}
});
}
}
if (completed) {
completeAfterTask(tcs, continuation, this, executor, ct);
}
return tcs.getTask();
}
/**
* Adds an asynchronous continuation to this task, returning a new task that completes after the
* task returned by the continuation has completed.
*/
public <TContinuationResult> Task<TContinuationResult> continueWithTask(
Continuation<TResult, Task<TContinuationResult>> continuation) {
return continueWithTask(continuation, IMMEDIATE_EXECUTOR, null);
}
/**
* Adds an asynchronous continuation to this task, returning a new task that completes after the
* task returned by the continuation has completed.
*/
public <TContinuationResult> Task<TContinuationResult> continueWithTask(
Continuation<TResult, Task<TContinuationResult>> continuation, CancellationToken ct) {
return continueWithTask(continuation, IMMEDIATE_EXECUTOR, ct);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception} or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccess(
final Continuation<TResult, TContinuationResult> continuation, Executor executor) {
return onSuccess(continuation, executor, null);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception} or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccess(
final Continuation<TResult, TContinuationResult> continuation, Executor executor,
final CancellationToken ct) {
return continueWithTask(new Continuation<TResult, Task<TContinuationResult>>() {
@Override
public Task<TContinuationResult> then(Task<TResult> task) {
if (ct != null && ct.isCancellationRequested()) {
return Task.cancelled();
}
if (task.isFaulted()) {
return Task.forError(task.getError());
} else if (task.isCancelled()) {
return Task.cancelled();
} else {
return task.continueWith(continuation);
}
}
}, executor);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccess(
final Continuation<TResult, TContinuationResult> continuation) {
return onSuccess(continuation, IMMEDIATE_EXECUTOR, null);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccess(
final Continuation<TResult, TContinuationResult> continuation, CancellationToken ct) {
return onSuccess(continuation, IMMEDIATE_EXECUTOR, ct);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(
final Continuation<TResult, Task<TContinuationResult>> continuation, Executor executor) {
return onSuccessTask(continuation, executor, null);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(
final Continuation<TResult, Task<TContinuationResult>> continuation, Executor executor,
final CancellationToken ct) {
return continueWithTask(new Continuation<TResult, Task<TContinuationResult>>() {
@Override
public Task<TContinuationResult> then(Task<TResult> task) {
if (ct != null && ct.isCancellationRequested()) {
return Task.cancelled();
}
if (task.isFaulted()) {
return Task.forError(task.getError());
} else if (task.isCancelled()) {
return Task.cancelled();
} else {
return task.continueWithTask(continuation);
}
}
}, executor);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(
final Continuation<TResult, Task<TContinuationResult>> continuation) {
return onSuccessTask(continuation, IMMEDIATE_EXECUTOR);
}
/**
* Runs a continuation when a task completes successfully, forwarding along
* {@link java.lang.Exception}s or cancellation.
*/
public <TContinuationResult> Task<TContinuationResult> onSuccessTask(
final Continuation<TResult, Task<TContinuationResult>> continuation,
CancellationToken ct) {
return onSuccessTask(continuation, IMMEDIATE_EXECUTOR, ct);
}
/**
* Handles the non-async (i.e. the continuation doesn't return a Task) continuation case, passing
* the results of the given Task through to the given continuation and using the results of that
* call to set the result of the TaskContinuationSource.
*
* @param tcs
* The TaskContinuationSource that will be orchestrated by this call.
* @param continuation
* The non-async continuation.
* @param task
* The task being completed.
* @param executor
* The executor to use when running the continuation (allowing the continuation to be
* scheduled on a different thread).
*/
private static <TContinuationResult, TResult> void completeImmediately(
final Task<TContinuationResult>.TaskCompletionSource tcs,
final Continuation<TResult, TContinuationResult> continuation, final Task<TResult> task,
Executor executor, final CancellationToken ct) {
executor.execute(new Runnable() {
@Override
public void run() {
if (ct != null && ct.isCancellationRequested()) {
tcs.setCancelled();
return;
}
try {
TContinuationResult result = continuation.then(task);
tcs.setResult(result);
} catch (CancellationException e) {
tcs.setCancelled();
} catch (Exception e) {
tcs.setError(e);
}
}
});
}
/**
* Handles the async (i.e. the continuation does return a Task) continuation case, passing the
* results of the given Task through to the given continuation to get a new Task. The
* TaskCompletionSource's results are only set when the new Task has completed, unwrapping the
* results of the task returned by the continuation.
*
* @param tcs
* The TaskContinuationSource that will be orchestrated by this call.
* @param continuation
* The async continuation.
* @param task
* The task being completed.
* @param executor
* The executor to use when running the continuation (allowing the continuation to be
* scheduled on a different thread).
*/
private static <TContinuationResult, TResult> void completeAfterTask(
final Task<TContinuationResult>.TaskCompletionSource tcs,
final Continuation<TResult, Task<TContinuationResult>> continuation,
final Task<TResult> task, final Executor executor,
final CancellationToken ct) {
executor.execute(new Runnable() {
@Override
public void run() {
if (ct != null && ct.isCancellationRequested()) {
tcs.setCancelled();
return;
}
try {
Task<TContinuationResult> result = continuation.then(task);
if (result == null) {
tcs.setResult(null);
} else {
result.continueWith(new Continuation<TContinuationResult, Void>() {
@Override
public Void then(Task<TContinuationResult> task) {
if (ct != null && ct.isCancellationRequested()) {
tcs.setCancelled();
return null;
}
if (task.isCancelled()) {
tcs.setCancelled();
} else if (task.isFaulted()) {
tcs.setError(task.getError());
} else {
tcs.setResult(task.getResult());
}
return null;
}
});
}
} catch (CancellationException e) {
tcs.setCancelled();
} catch (Exception e) {
tcs.setError(e);
}
}
});
}
private void runContinuations() {
synchronized (lock) {
for (Continuation<TResult, ?> continuation : continuations) {
try {
continuation.then(this);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
continuations = null;
}
}
/**
* Allows safe orchestration of a task's completion, preventing the consumer from prematurely
* completing the task. Essentially, it represents the producer side of a Task<TResult>, providing
* access to the consumer side through the getTask() method while isolating the Task's completion
* mechanisms from the consumer.
*/
public class TaskCompletionSource {
private TaskCompletionSource() {
}
/**
* @return the Task associated with this TaskCompletionSource.
*/
public Task<TResult> getTask() {
return Task.this;
}
/**
* Sets the cancelled flag on the Task if the Task hasn't already been completed.
*/
public boolean trySetCancelled() {
synchronized (lock) {
if (complete) {
return false;
}
complete = true;
cancelled = true;
lock.notifyAll();
runContinuations();
return true;
}
}
/**
* Sets the result on the Task if the Task hasn't already been completed.
*/
public boolean trySetResult(TResult result) {
synchronized (lock) {
if (complete) {
return false;
}
complete = true;
Task.this.result = result;
lock.notifyAll();
runContinuations();
return true;
}
}
/**
* Sets the error on the Task if the Task hasn't already been completed.
*/
public boolean trySetError(Exception error) {
synchronized (lock) {
if (complete) {
return false;
}
complete = true;
Task.this.error = error;
lock.notifyAll();
runContinuations();
return true;
}
}
/**
* Sets the cancelled flag on the task, throwing if the Task has already been completed.
*/
public void setCancelled() {
if (!trySetCancelled()) {
throw new IllegalStateException("Cannot cancel a completed task.");
}
}
/**
* Sets the result of the Task, throwing if the Task has already been completed.
*/
public void setResult(TResult result) {
if (!trySetResult(result)) {
throw new IllegalStateException("Cannot set the result of a completed task.");
}
}
/**
* Sets the error of the Task, throwing if the Task has already been completed.
*/
public void setError(Exception error) {
if (!trySetError(error)) {
throw new IllegalStateException("Cannot set the error on a completed task.");
}
}
}
}