Search in sources :

Example 1 with AsyncCallable

use of com.google.common.util.concurrent.AsyncCallable in project android_packages_apps_Dialer by LineageOS.

the class DialerFutureSerializer method submitAsync.

/**
 * Enqueues a task to run when the previous task (if any) completes.
 *
 * <p>Cancellation does not propagate from the output future to the future returned from {@code
 * callable}, but if the output future is cancelled before {@link AsyncCallable#call()} is
 * invoked, {@link AsyncCallable#call()} will not be invoked.
 */
public <T> ListenableFuture<T> submitAsync(final AsyncCallable<T> callable, Executor executor) {
    AtomicBoolean wasCancelled = new AtomicBoolean(false);
    final AsyncCallable<T> task = () -> {
        if (wasCancelled.get()) {
            return immediateCancelledFuture();
        }
        return callable.call();
    };
    /*
     * Three futures are at play here:
     * taskFuture is the future that comes from the callable.
     * newFuture is the future we use to track the serialization of our task.
     * oldFuture is the previous task's newFuture.
     *
     * newFuture is guaranteed to only complete once all tasks previously submitted to this instance
     * once the futures returned from those submissions have completed.
     */
    final SettableFuture<Object> newFuture = SettableFuture.create();
    final ListenableFuture<?> oldFuture = ref.getAndSet(newFuture);
    // Invoke our task once the previous future completes.
    final ListenableFuture<T> taskFuture = Futures.nonCancellationPropagating(Futures.submitAsync(task, runnable -> oldFuture.addListener(runnable, executor)));
    // newFuture's lifetime is determined by taskFuture, unless taskFuture is cancelled, in which
    // case it falls back to oldFuture's. This is to ensure that if the future we return is
    // cancelled, we don't begin execution of the next task until after oldFuture completes.
    taskFuture.addListener(() -> {
        if (taskFuture.isCancelled()) {
            // Since the value of oldFuture can only ever be immediateFuture(null) or setFuture of a
            // future that eventually came from immediateFuture(null), this doesn't leak throwables
            // or completion values.
            wasCancelled.set(true);
            newFuture.setFuture(oldFuture);
        } else {
            newFuture.set(null);
        }
    }, directExecutor());
    return taskFuture;
}
Also used : Futures.immediateFuture(com.google.common.util.concurrent.Futures.immediateFuture) Futures(com.google.common.util.concurrent.Futures) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) Executor(java.util.concurrent.Executor) Futures.immediateCancelledFuture(com.google.common.util.concurrent.Futures.immediateCancelledFuture) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) Callable(java.util.concurrent.Callable) SettableFuture(com.google.common.util.concurrent.SettableFuture) AtomicReference(java.util.concurrent.atomic.AtomicReference) MoreExecutors.directExecutor(com.google.common.util.concurrent.MoreExecutors.directExecutor) AsyncCallable(com.google.common.util.concurrent.AsyncCallable) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean)

Aggregations

AsyncCallable (com.google.common.util.concurrent.AsyncCallable)1 Futures (com.google.common.util.concurrent.Futures)1 Futures.immediateCancelledFuture (com.google.common.util.concurrent.Futures.immediateCancelledFuture)1 Futures.immediateFuture (com.google.common.util.concurrent.Futures.immediateFuture)1 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)1 MoreExecutors.directExecutor (com.google.common.util.concurrent.MoreExecutors.directExecutor)1 SettableFuture (com.google.common.util.concurrent.SettableFuture)1 Callable (java.util.concurrent.Callable)1 Executor (java.util.concurrent.Executor)1 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)1 AtomicReference (java.util.concurrent.atomic.AtomicReference)1