package io.airlift.concurrent; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import io.airlift.units.Duration; import javax.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.List; import java.util.Optional; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionStage; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Throwables.propagateIfPossible; import static com.google.common.base.Throwables.throwIfInstanceOf; import static com.google.common.collect.Iterables.isEmpty; import static com.google.common.util.concurrent.Futures.catchingAsync; import static com.google.common.util.concurrent.Futures.immediateFailedFuture; import static com.google.common.util.concurrent.Futures.immediateFuture; import static com.google.common.util.concurrent.Futures.withTimeout; import static com.google.common.util.concurrent.MoreExecutors.directExecutor; import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.MILLISECONDS; public final class MoreFutures { private MoreFutures() { } /** * Cancels the destination Future if the source Future is cancelled. */ public static <X, Y> void propagateCancellation(ListenableFuture<? extends X> source, ListenableFuture<? extends Y> destination, boolean mayInterruptIfRunning) { source.addListener(() -> { if (source.isCancelled()) { destination.cancel(mayInterruptIfRunning); } }, directExecutor()); } /** * Mirrors all results of the source Future to the destination Future. * * This also propagates cancellations from the destination Future back to the source Future. */ public static <T> void mirror(ListenableFuture<? extends T> source, SettableFuture<? super T> destination, boolean mayInterruptIfRunning) { Futures.addCallback(source, new FutureCallback<T>() { @Override public void onSuccess(@Nullable T result) { destination.set(result); } @Override public void onFailure(Throwable t) { destination.setException(t); } }); propagateCancellation(destination, source, mayInterruptIfRunning); } /** * Attempts to unwrap a throwable that has been wrapped in a {@link CompletionException}. */ public static Throwable unwrapCompletionException(Throwable throwable) { if (throwable instanceof CompletionException) { return firstNonNull(throwable.getCause(), throwable); } return throwable; } /** * Returns a future that can not be completed or canceled. */ @Deprecated public static <V> CompletableFuture<V> unmodifiableFuture(CompletableFuture<V> future) { return unmodifiableFuture(future, false); } /** * Returns a future that can not be completed or optionally canceled. */ @Deprecated public static <V> CompletableFuture<V> unmodifiableFuture(CompletableFuture<V> future, boolean propagateCancel) { requireNonNull(future, "future is null"); Function<Boolean, Boolean> onCancelFunction; if (propagateCancel) { onCancelFunction = future::cancel; } else { onCancelFunction = mayInterrupt -> false; } UnmodifiableCompletableFuture<V> unmodifiableFuture = new UnmodifiableCompletableFuture<>(onCancelFunction); future.whenComplete((value, exception) -> { if (exception != null) { unmodifiableFuture.internalCompleteExceptionally(exception); } else { unmodifiableFuture.internalComplete(value); } }); return unmodifiableFuture; } /** * Returns a failed future containing the specified throwable. */ @Deprecated public static <V> CompletableFuture<V> failedFuture(Throwable throwable) { requireNonNull(throwable, "throwable is null"); CompletableFuture<V> future = new CompletableFuture<>(); future.completeExceptionally(throwable); return future; } /** * Waits for the value from the future. If the future is failed, the exception * is thrown directly if unchecked or wrapped in a RuntimeException. If the * thread is interrupted, the thread interruption flag is set and the original * InterruptedException is wrapped in a RuntimeException and thrown. */ public static <V> V getFutureValue(Future<V> future) { return getFutureValue(future, RuntimeException.class); } /** * Waits for the value from the future. If the future is failed, the exception * is thrown directly if it is an instance of the specified exception type or * unchecked, or it is wrapped in a RuntimeException. If the thread is * interrupted, the thread interruption flag is set and the original * InterruptedException is wrapped in a RuntimeException and thrown. */ public static <V, E extends Exception> V getFutureValue(Future<V> future, Class<E> exceptionType) throws E { requireNonNull(future, "future is null"); requireNonNull(exceptionType, "exceptionType is null"); try { return future.get(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("interrupted", e); } catch (ExecutionException e) { Throwable cause = e.getCause() == null ? e : e.getCause(); propagateIfPossible(cause, exceptionType); throw new RuntimeException(cause); } } /** * Gets the current value of the future without waiting. If the future * value is null, an empty Optional is still returned, and in this case the caller * must check the future directly for the null value. */ public static <T> Optional<T> tryGetFutureValue(Future<T> future) { requireNonNull(future, "future is null"); if (!future.isDone()) { return Optional.empty(); } return tryGetFutureValue(future, 0, MILLISECONDS); } /** * Waits for the the value from the future for the specified time. If the future * value is null, an empty Optional is still returned, and in this case the caller * must check the future directly for the null value. If the future is failed, * the exception is thrown directly if unchecked or wrapped in a RuntimeException. * If the thread is interrupted, the thread interruption flag is set and the original * InterruptedException is wrapped in a RuntimeException and thrown. */ public static <V> Optional<V> tryGetFutureValue(Future<V> future, int timeout, TimeUnit timeUnit) { return tryGetFutureValue(future, timeout, timeUnit, RuntimeException.class); } /** * Waits for the the value from the future for the specified time. If the future * value is null, an empty Optional is still returned, and in this case the caller * must check the future directly for the null value. If the future is failed, * the exception is thrown directly if it is an instance of the specified exception * type or unchecked, or it is wrapped in a RuntimeException. If the thread is * interrupted, the thread interruption flag is set and the original * InterruptedException is wrapped in a RuntimeException and thrown. */ public static <V, E extends Exception> Optional<V> tryGetFutureValue(Future<V> future, int timeout, TimeUnit timeUnit, Class<E> exceptionType) throws E { requireNonNull(future, "future is null"); checkArgument(timeout >= 0, "timeout is negative"); requireNonNull(timeUnit, "timeUnit is null"); requireNonNull(exceptionType, "exceptionType is null"); try { return Optional.ofNullable(future.get(timeout, timeUnit)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException("interrupted", e); } catch (ExecutionException e) { Throwable cause = e.getCause() == null ? e : e.getCause(); propagateIfPossible(cause, exceptionType); throw new RuntimeException(cause); } catch (TimeoutException expected) { // expected } return Optional.empty(); } /** * Creates a future that completes when the first future completes either normally * or exceptionally. Cancellation of the future propagates to the supplied futures. */ public static <V> ListenableFuture<V> whenAnyComplete(Iterable<? extends ListenableFuture<? extends V>> futures) { requireNonNull(futures, "futures is null"); checkArgument(!isEmpty(futures), "futures is empty"); ExtendedSettableFuture<V> firstCompletedFuture = ExtendedSettableFuture.create(); for (ListenableFuture<? extends V> future : futures) { firstCompletedFuture.setAsync(future); } return firstCompletedFuture; } /** * Creates a future that completes when the first future completes either normally * or exceptionally. Cancellation of the future does not propagate to the supplied * futures. */ @Deprecated public static <V> CompletableFuture<V> firstCompletedFuture(Iterable<? extends CompletionStage<? extends V>> futures) { return firstCompletedFuture(futures, false); } /** * Creates a future that completes when the first future completes either normally * or exceptionally. Cancellation of the future will optionally propagate to the * supplied futures. */ @Deprecated public static <V> CompletableFuture<V> firstCompletedFuture(Iterable<? extends CompletionStage<? extends V>> futures, boolean propagateCancel) { requireNonNull(futures, "futures is null"); checkArgument(!isEmpty(futures), "futures is empty"); CompletableFuture<V> future = new CompletableFuture<>(); for (CompletionStage<? extends V> stage : futures) { stage.whenComplete((value, exception) -> { if (exception != null) { future.completeExceptionally(exception); } else { future.complete(value); } }); } if (propagateCancel) { future.exceptionally(throwable -> { if (throwable instanceof CancellationException) { for (CompletionStage<? extends V> sourceFuture : futures) { if (sourceFuture instanceof Future) { ((Future<?>) sourceFuture).cancel(true); } } } return null; }); } return future; } /** * Returns an unmodifiable future that is completed when all of the given * futures complete. If any of the given futures complete exceptionally, then the * returned future also does so immediately, with a CompletionException holding this exception * as its cause. Otherwise, the results of the given futures are reflected in the * returned future as a list of results matching the input order. If no futures are * provided, returns a future completed with an empty list. */ @Deprecated public static <V> CompletableFuture<List<V>> allAsList(List<CompletableFuture<? extends V>> futures) { CompletableFuture<Void> allDoneFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])); // Eagerly propagate exceptions, rather than waiting for all the futures to complete first (default behavior) for (CompletableFuture<? extends V> future : futures) { future.whenComplete((v, throwable) -> { if (throwable != null) { allDoneFuture.completeExceptionally(throwable); } }); } return unmodifiableFuture(allDoneFuture.thenApply(v -> futures.stream(). map(CompletableFuture::join). collect(Collectors.<V>toList()))); } /** * Returns a new future that is completed when the supplied future completes or * when the timeout expires. If the timeout occurs or the returned CompletableFuture * is canceled, the supplied future will be canceled. */ public static <T> ListenableFuture<T> addTimeout(ListenableFuture<T> future, Callable<T> onTimeout, Duration timeout, ScheduledExecutorService executorService) { return catchingAsync(withTimeout(future, timeout.toMillis(), MILLISECONDS, executorService), TimeoutException.class, timeoutException -> { try { return immediateFuture(onTimeout.call()); } catch (Throwable throwable) { return immediateFailedFuture(throwable); } }); } /** * Returns a new future that is completed when the supplied future completes or * when the timeout expires. If the timeout occurs or the returned CompletableFuture * is canceled, the supplied future will be canceled. */ @Deprecated public static <T> CompletableFuture<T> addTimeout(CompletableFuture<T> future, Callable<T> onTimeout, Duration timeout, ScheduledExecutorService executorService) { requireNonNull(future, "future is null"); requireNonNull(onTimeout, "timeoutValue is null"); requireNonNull(timeout, "timeout is null"); requireNonNull(executorService, "executorService is null"); // if the future is already complete, just return it if (future.isDone()) { return future; } // create an unmodifiable future that propagates cancel // down cast is safe because this is our code UnmodifiableCompletableFuture<T> futureWithTimeout = (UnmodifiableCompletableFuture<T>) unmodifiableFuture(future, true); // schedule a task to complete the future when the time expires ScheduledFuture<?> timeoutTaskFuture = executorService.schedule(new TimeoutFutureTask<>(futureWithTimeout, onTimeout, future), timeout.toMillis(), MILLISECONDS); // when future completes, cancel the timeout task future.whenCompleteAsync((value, exception) -> timeoutTaskFuture.cancel(false), executorService); return futureWithTimeout; } /** * Converts a ListenableFuture to a CompletableFuture. Cancellation of the * CompletableFuture will be propagated to the ListenableFuture. */ public static <V> CompletableFuture<V> toCompletableFuture(ListenableFuture<V> listenableFuture) { requireNonNull(listenableFuture, "listenableFuture is null"); CompletableFuture<V> future = new CompletableFuture<>(); future.exceptionally(throwable -> { if (throwable instanceof CancellationException) { listenableFuture.cancel(true); } return null; }); Futures.addCallback(listenableFuture, new FutureCallback<V>() { @Override public void onSuccess(V result) { future.complete(result); } @Override public void onFailure(Throwable t) { future.completeExceptionally(t); } }); return future; } /** * Converts a CompletableFuture to a ListenableFuture. Cancellation of the * ListenableFuture will be propagated to the CompletableFuture. */ public static <V> ListenableFuture<V> toListenableFuture(CompletableFuture<V> completableFuture) { requireNonNull(completableFuture, "completableFuture is null"); SettableFuture<V> future = SettableFuture.create(); Futures.addCallback(future, new FutureCallback<V>() { @Override public void onSuccess(V result) { } @Override public void onFailure(Throwable throwable) { if (throwable instanceof CancellationException) { completableFuture.cancel(true); } } }); completableFuture.whenComplete((value, exception) -> { if (exception != null) { future.setException(exception); } else { future.set(value); } }); return future; } public static <T> void addExceptionCallback(ListenableFuture<T> result, Consumer<Throwable> exceptionCallback) { Futures.addCallback(result, new FutureCallback<T>() { @Override public void onSuccess(@Nullable T result) { } @Override public void onFailure(Throwable t) { exceptionCallback.accept(t); } }); } public static <T> void addExceptionCallback(ListenableFuture<T> result, Runnable exceptionCallback) { Futures.addCallback(result, new FutureCallback<T>() { @Override public void onSuccess(@Nullable T result) { } @Override public void onFailure(Throwable t) { exceptionCallback.run(); } }); } private static class UnmodifiableCompletableFuture<V> extends CompletableFuture<V> { private final Function<Boolean, Boolean> onCancel; public UnmodifiableCompletableFuture(Function<Boolean, Boolean> onCancel) { this.onCancel = requireNonNull(onCancel, "onCancel is null"); } void internalComplete(V value) { super.complete(value); } void internalCompleteExceptionally(Throwable ex) { super.completeExceptionally(ex); } @Override public boolean cancel(boolean mayInterruptIfRunning) { return onCancel.apply(mayInterruptIfRunning); } @Override public boolean complete(V value) { throw new UnsupportedOperationException(); } @Override public boolean completeExceptionally(Throwable ex) { if (ex instanceof CancellationException) { return cancel(false); } throw new UnsupportedOperationException(); } @Override public void obtrudeValue(V value) { throw new UnsupportedOperationException(); } @Override public void obtrudeException(Throwable ex) { throw new UnsupportedOperationException(); } } private static class TimeoutFutureTask<T> implements Runnable { private final UnmodifiableCompletableFuture<T> settableFuture; private final Callable<T> timeoutValue; private final WeakReference<CompletableFuture<T>> futureReference; public TimeoutFutureTask(UnmodifiableCompletableFuture<T> settableFuture, Callable<T> timeoutValue, CompletableFuture<T> future) { this.settableFuture = settableFuture; this.timeoutValue = timeoutValue; // the scheduled executor can hold on to the timeout task for a long time, and // the future can reference large expensive objects. Since we are only interested // in canceling this future on a timeout, only hold a weak reference to the future this.futureReference = new WeakReference<>(future); } @Override public void run() { if (settableFuture.isDone()) { return; } // run the timeout task and set the result into the future try { T result = timeoutValue.call(); settableFuture.internalComplete(result); } catch (Throwable t) { settableFuture.internalCompleteExceptionally(t); throwIfInstanceOf(t, RuntimeException.class); } // cancel the original future, if it still exists Future<T> future = futureReference.get(); if (future != null) { future.cancel(true); } } } }