All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.prestosql.jdbc.$internal.airlift.concurrent.MoreFutures Maven / Gradle / Ivy

The newest version!
package io.prestosql.jdbc.$internal.airlift.concurrent;

import io.prestosql.jdbc.$internal.guava.util.concurrent.AsyncFunction;
import io.prestosql.jdbc.$internal.guava.util.concurrent.FluentFuture;
import io.prestosql.jdbc.$internal.guava.util.concurrent.FutureCallback;
import io.prestosql.jdbc.$internal.guava.util.concurrent.Futures;
import io.prestosql.jdbc.$internal.guava.util.concurrent.ListenableFuture;
import io.prestosql.jdbc.$internal.guava.util.concurrent.SettableFuture;
import io.prestosql.jdbc.$internal.airlift.units.Duration;

import io.prestosql.jdbc.$internal.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.Executor;
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 io.prestosql.jdbc.$internal.guava.base.MoreObjects.firstNonNull;
import static io.prestosql.jdbc.$internal.guava.base.Preconditions.checkArgument;
import static io.prestosql.jdbc.$internal.guava.base.Throwables.propagateIfPossible;
import static io.prestosql.jdbc.$internal.guava.base.Throwables.throwIfInstanceOf;
import static io.prestosql.jdbc.$internal.guava.collect.Iterables.isEmpty;
import static io.prestosql.jdbc.$internal.guava.util.concurrent.Futures.immediateFailedFuture;
import static io.prestosql.jdbc.$internal.guava.util.concurrent.Futures.immediateFuture;
import static io.prestosql.jdbc.$internal.guava.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  void propagateCancellation(ListenableFuture source, Future 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 void mirror(ListenableFuture source, SettableFuture destination, boolean mayInterruptIfRunning) { FutureCallback callback = new FutureCallback() { @Override public void onSuccess(@Nullable T result) { destination.set(result); } @Override public void onFailure(Throwable t) { destination.setException(t); } }; Futures.addCallback(source, callback, directExecutor()); 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 CompletableFuture unmodifiableFuture(CompletableFuture future) { return unmodifiableFuture(future, false); } /** * Returns a future that can not be completed or optionally canceled. */ @Deprecated public static CompletableFuture unmodifiableFuture(CompletableFuture future, boolean propagateCancel) { requireNonNull(future, "future is null"); Function onCancelFunction; if (propagateCancel) { onCancelFunction = future::cancel; } else { onCancelFunction = mayInterrupt -> false; } UnmodifiableCompletableFuture 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 CompletableFuture failedFuture(Throwable throwable) { requireNonNull(throwable, "throwable is null"); CompletableFuture 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 getFutureValue(Future 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 getFutureValue(Future future, Class 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 Optional tryGetFutureValue(Future 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 Optional tryGetFutureValue(Future 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 Optional tryGetFutureValue(Future future, int timeout, TimeUnit timeUnit, Class 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(); } /** * Returns the result of the input {@link Future}, which must have already completed. *

* Similar to Futures{@link Futures#getDone(Future)}, but does not throw checked exceptions. */ public static T getDone(Future future) { requireNonNull(future, "future is null"); checkArgument(future.isDone(), "future not done yet"); return getFutureValue(future); } /** * Checks that the completed future completed successfully. */ public static void checkSuccess(Future future, String errorMessage) { requireNonNull(future, "future is null"); requireNonNull(errorMessage, "errorMessage is null"); checkArgument(future.isDone(), "future not done yet"); try { getFutureValue(future); } catch (RuntimeException e) { throw new IllegalArgumentException(errorMessage, e); } } /** * 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 ListenableFuture whenAnyComplete(Iterable> futures) { requireNonNull(futures, "futures is null"); checkArgument(!isEmpty(futures), "futures is empty"); ExtendedSettableFuture firstCompletedFuture = ExtendedSettableFuture.create(); for (ListenableFuture future : futures) { firstCompletedFuture.setAsync(future); } return firstCompletedFuture; } /** * Creates a future that completes when the first future completes either normally * or exceptionally. All other futures are cancelled when one completes. * Cancellation of the returned future propagates to the supplied futures. *

* It is critical for the performance of this function that * {@code guava.concurrent.generate_cancellation_cause} is false, * which is the default since Guava v20. */ public static ListenableFuture whenAnyCompleteCancelOthers(Iterable> futures) { requireNonNull(futures, "futures is null"); checkArgument(!isEmpty(futures), "futures is empty"); // wait for the first task to unblock and then cancel all futures to free up resources ListenableFuture anyComplete = whenAnyComplete(futures); anyComplete.addListener( () -> { for (ListenableFuture future : futures) { future.cancel(true); } }, directExecutor()); return anyComplete; } /** * 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 CompletableFuture firstCompletedFuture(Iterable> 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 CompletableFuture firstCompletedFuture(Iterable> futures, boolean propagateCancel) { requireNonNull(futures, "futures is null"); checkArgument(!isEmpty(futures), "futures is empty"); CompletableFuture future = new CompletableFuture<>(); for (CompletionStage 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 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 CompletableFuture> allAsList(List> futures) { CompletableFuture 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 future : futures) { future.whenComplete((v, throwable) -> { if (throwable != null) { allDoneFuture.completeExceptionally(throwable); } }); } return unmodifiableFuture(allDoneFuture.thenApply(v -> futures.stream() .map(CompletableFuture::join) .collect(Collectors.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 ListenableFuture addTimeout(ListenableFuture future, Callable onTimeout, Duration timeout, ScheduledExecutorService executorService) { AsyncFunction timeoutHandler = timeoutException -> { try { return immediateFuture(onTimeout.call()); } catch (Throwable throwable) { return immediateFailedFuture(throwable); } }; return FluentFuture.from(future) .withTimeout(timeout.toMillis(), MILLISECONDS, executorService) .catchingAsync(TimeoutException.class, timeoutHandler, directExecutor()); } /** * 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 CompletableFuture addTimeout(CompletableFuture future, Callable 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 futureWithTimeout = (UnmodifiableCompletableFuture) 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 CompletableFuture toCompletableFuture(ListenableFuture listenableFuture) { requireNonNull(listenableFuture, "listenableFuture is null"); CompletableFuture future = new CompletableFuture<>(); future.exceptionally(throwable -> { if (throwable instanceof CancellationException) { listenableFuture.cancel(true); } return null; }); FutureCallback callback = new FutureCallback() { @Override public void onSuccess(V result) { future.complete(result); } @Override public void onFailure(Throwable t) { future.completeExceptionally(t); } }; Futures.addCallback(listenableFuture, callback, directExecutor()); return future; } /** * Converts a CompletableFuture to a ListenableFuture. Cancellation of the * ListenableFuture will be propagated to the CompletableFuture. */ public static ListenableFuture toListenableFuture(CompletableFuture completableFuture) { requireNonNull(completableFuture, "completableFuture is null"); SettableFuture future = SettableFuture.create(); propagateCancellation(future, completableFuture, true); completableFuture.whenComplete((value, exception) -> { if (exception != null) { future.setException(exception); } else { future.set(value); } }); return future; } /** * Invokes the callback if the future completes successfully. Note, this uses the direct * executor, so the callback should not be resource intensive. */ public static void addSuccessCallback(ListenableFuture future, Consumer successCallback) { addSuccessCallback(future, successCallback, directExecutor()); } /** * Invokes the callback, using the specified executor, if the future completes successfully. */ public static void addSuccessCallback(ListenableFuture future, Consumer successCallback, Executor executor) { requireNonNull(future, "future is null"); requireNonNull(successCallback, "successCallback is null"); FutureCallback callback = new FutureCallback() { @Override public void onSuccess(@Nullable T result) { successCallback.accept(result); } @Override public void onFailure(Throwable t) {} }; Futures.addCallback(future, callback, executor); } /** * Invokes the callback if the future completes successfully. Note, this uses the direct * executor, so the callback should not be resource intensive. */ public static void addSuccessCallback(ListenableFuture future, Runnable successCallback) { addSuccessCallback(future, successCallback, directExecutor()); } /** * Invokes the callback, using the specified executor, if the future completes successfully. */ public static void addSuccessCallback(ListenableFuture future, Runnable successCallback, Executor executor) { requireNonNull(successCallback, "successCallback is null"); addSuccessCallback(future, t -> successCallback.run(), executor); } /** * Invokes the callback if the future fails. Note, this uses the direct * executor, so the callback should not be resource intensive. */ public static void addExceptionCallback(ListenableFuture future, Consumer exceptionCallback) { addExceptionCallback(future, exceptionCallback, directExecutor()); } /** * Invokes the callback, using the specified executor, if the future fails. */ public static void addExceptionCallback(ListenableFuture future, Consumer exceptionCallback, Executor executor) { requireNonNull(future, "future is null"); requireNonNull(exceptionCallback, "exceptionCallback is null"); FutureCallback callback = new FutureCallback() { @Override public void onSuccess(@Nullable T result) {} @Override public void onFailure(Throwable t) { exceptionCallback.accept(t); } }; Futures.addCallback(future, callback, executor); } /** * Invokes the callback if the future fails. Note, this uses the direct * executor, so the callback should not be resource intensive. */ public static void addExceptionCallback(ListenableFuture future, Runnable exceptionCallback) { addExceptionCallback(future, exceptionCallback, directExecutor()); } /** * Invokes the callback, using the specified executor, if the future fails. */ public static void addExceptionCallback(ListenableFuture future, Runnable exceptionCallback, Executor executor) { requireNonNull(exceptionCallback, "exceptionCallback is null"); addExceptionCallback(future, t -> exceptionCallback.run(), executor); } private static class UnmodifiableCompletableFuture extends CompletableFuture { private final Function onCancel; public UnmodifiableCompletableFuture(Function 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 implements Runnable { private final UnmodifiableCompletableFuture settableFuture; private final Callable timeoutValue; private final WeakReference> futureReference; public TimeoutFutureTask(UnmodifiableCompletableFuture settableFuture, Callable timeoutValue, CompletableFuture 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 future = futureReference.get(); if (future != null) { future.cancel(true); } } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy