package org.threadly.concurrent.future; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; import java.util.function.Predicate; import org.threadly.concurrent.SameThreadSubmitterExecutor; import org.threadly.concurrent.SubmitterScheduler; import org.threadly.concurrent.collections.ConcurrentArrayList; import org.threadly.util.ArgumentVerifier; import org.threadly.util.Clock; import org.threadly.util.SuppressedStackRuntimeException; /** * A collection of small utilities for handling futures. This class has lots of tools for dealing * with collections of futures, either blocking, extracting results, and more. * <p> * Generating already done futures: * <ul> * <li>{@link #immediateResultFuture(Object)} * <li>{@link #immediateFailureFuture(Throwable)} * </ul> * <p> * Tools for blocking: * <ul> * <li>{@link #blockTillAllComplete(Iterable)} * <li>{@link #blockTillAllComplete(Iterable, long)} * <li>{@link #blockTillAllCompleteOrFirstError(Iterable)} * <li>{@link #blockTillAllCompleteOrFirstError(Iterable, long)} * </ul> * <p> * Tools for manipulating collections of futures: * <ul> * <li>{@link #cancelIncompleteFutures(Iterable, boolean)} * <li>{@link #cancelIncompleteFuturesIfAnyFail(boolean, Iterable, boolean)} * <li>{@link #countFuturesWithResult(Iterable, Object)} * <li>{@link #countFuturesWithResult(Iterable, Object, long)} * <li>{@link #makeCompleteFuture(Iterable)} * <li>{@link #makeCompleteFuture(Iterable, Object)} * <li>{@link #makeFailurePropagatingCompleteFuture(Iterable)} * <li>{@link #makeFailurePropagatingCompleteFuture(Iterable, Object)} * <li>{@link #makeCompleteListFuture(Iterable)} * <li>{@link #makeFailureListFuture(Iterable)} * <li>{@link #makeResultListFuture(Iterable, boolean)} * <li>{@link #makeSuccessListFuture(Iterable)} * </ul> * <p> * Retry operation and return final result in future: * <ul> * <li>{@link #scheduleWhileTaskResultNull(SubmitterScheduler, long, boolean, Callable)} * <li>{@link #scheduleWhileTaskResultNull(SubmitterScheduler, long, boolean, Callable, long)} * <li>{@link #scheduleWhile(SubmitterScheduler, long, boolean, Callable, Predicate)} * <li>{@link #scheduleWhile(SubmitterScheduler, long, boolean, Callable, Predicate, long)} * <li>{@link #scheduleWhile(SubmitterScheduler, long, ListenableFuture, Callable, Predicate)} * <li>{@link #scheduleWhile(SubmitterScheduler, long, ListenableFuture, Callable, Predicate, long)} * </ul> * * @since 1.0.0 */ public class FutureUtils { /** * This call blocks till all futures in the list have completed. If the future completed with * an error, the {@link ExecutionException} is swallowed. Meaning that this does not attempt to * verify that all futures completed successfully. If you need to know if any failed, please * use {@link #blockTillAllCompleteOrFirstError(Iterable)}. * <p> * If you need to specify a timeout to control how long to block, consider using * {@link #blockTillAllComplete(Iterable, long)}. * * @param futures Structure of futures to iterate over * @throws InterruptedException Thrown if thread is interrupted while waiting on future */ public static void blockTillAllComplete(Iterable<? extends Future<?>> futures) throws InterruptedException { countFuturesWithResult(futures, null); } /** * This call blocks till all futures in the list have completed. If the future completed with * an error, the {@link ExecutionException} is swallowed. Meaning that this does not attempt to * verify that all futures completed successfully. If you need to know if any failed, please * use {@link #blockTillAllCompleteOrFirstError(Iterable, long)}. * * @since 4.0.0 * @param futures Structure of futures to iterate over * @param timeoutInMillis timeout to wait for futures to complete in milliseconds * @throws InterruptedException Thrown if thread is interrupted while waiting on future * @throws TimeoutException Thrown if the timeout elapsed while waiting on futures to complete */ public static void blockTillAllComplete(Iterable<? extends Future<?>> futures, long timeoutInMillis) throws InterruptedException, TimeoutException { countFuturesWithResult(futures, null, timeoutInMillis); } /** * This call blocks till all futures in the list have completed. If the future completed with * an error an {@link ExecutionException} is thrown. If this exception is thrown, all futures * may or may not be completed, the exception is thrown as soon as it is hit. There also may be * additional futures that errored (but were not hit yet). * <p> * If you need to specify a timeout to control how long to block, consider using * {@link #blockTillAllCompleteOrFirstError(Iterable, long)}. * * @param futures Structure of futures to iterate over * @throws InterruptedException Thrown if thread is interrupted while waiting on future * @throws ExecutionException Thrown if future throws exception on .get() call */ public static void blockTillAllCompleteOrFirstError(Iterable<? extends Future<?>> futures) throws InterruptedException, ExecutionException { if (futures == null) { return; } Iterator<? extends Future<?>> it = futures.iterator(); while (it.hasNext()) { it.next().get(); } } /** * This call blocks till all futures in the list have completed. If the future completed with * an error an {@link ExecutionException} is thrown. If this exception is thrown, all futures * may or may not be completed, the exception is thrown as soon as it is hit. There also may be * additional futures that errored (but were not hit yet). * * @since 4.0.0 * @param futures Structure of futures to iterate over * @param timeoutInMillis timeout to wait for futures to complete in milliseconds * @throws InterruptedException Thrown if thread is interrupted while waiting on future * @throws TimeoutException Thrown if the timeout elapsed while waiting on futures to complete * @throws ExecutionException Thrown if future throws exception on .get() call */ public static void blockTillAllCompleteOrFirstError(Iterable<? extends Future<?>> futures, long timeoutInMillis) throws InterruptedException, TimeoutException, ExecutionException { if (futures == null) { return; } Iterator<? extends Future<?>> it = futures.iterator(); long startTime = Clock.accurateForwardProgressingMillis(); long remainingTime; while (it.hasNext() && (remainingTime = timeoutInMillis - (Clock.lastKnownForwardProgressingMillis() - startTime)) > 0) { it.next().get(remainingTime, TimeUnit.MILLISECONDS); } if (it.hasNext()) { throw new TimeoutException(); } } /** * Counts how many futures provided completed with a result that matches the one provided here. * This can be most useful if your looking to know if an error occurred that was not an * {@link ExecutionException}. For example assume an API return's {@code Future<Boolean>} and a * {@code false} represents a failure, this can be used to look for those types of error * results. * <p> * Just like {@link #blockTillAllComplete(Iterable)}, this will block until all futures have * completed (so we can verify if their result matches or not). * <p> * If you need to specify a timeout to control how long to block, consider using * {@link #countFuturesWithResult(Iterable, Object, long)}. * * @since 4.0.0 * @param <T> type of result futures provide to compare against * @param futures Structure of futures to iterate over * @param comparisonResult Object to compare future results against to look for match * @return Number of futures which match the result using a {@link Object#equals(Object)} comparison * @throws InterruptedException Thrown if thread is interrupted while waiting on future's result */ public static <T> int countFuturesWithResult(Iterable<? extends Future<?>> futures, T comparisonResult) throws InterruptedException { if (futures == null) { return 0; } int resultCount = 0; Iterator<? extends Future<?>> it = futures.iterator(); while (it.hasNext()) { Future<?> f = it.next(); if (f.isCancelled()) { continue; } try { if (comparisonResult == null) { if (f.get() == null) { resultCount++; } } else if (comparisonResult.equals(f.get())) { resultCount++; } } catch (CancellationException e) { // swallowed } catch (ExecutionException e) { // swallowed } } return resultCount; } /** * Counts how many futures provided completed with a result that matches the one provided here. * This can be most useful if your looking to know if an error occurred that was not an * {@link ExecutionException}. For example assume an API return's {@code Future<Boolean>} and a * {@code false} represents a failure, this can be used to look for those types of error * results. * <p> * Just like {@link #blockTillAllComplete(Iterable)}, this will block until all futures have * completed (so we can verify if their result matches or not). * * @since 4.0.0 * @param <T> type of result futures provide to compare against * @param futures Structure of futures to iterate over * @param comparisonResult Object to compare future results against to look for match * @param timeoutInMillis timeout to wait for futures to complete in milliseconds * @return Number of futures which match the result using a {@link Object#equals(Object)} comparison * @throws InterruptedException Thrown if thread is interrupted while waiting on future's result * @throws TimeoutException Thrown if the timeout elapsed while waiting on futures to complete */ public static <T> int countFuturesWithResult(Iterable<? extends Future<?>> futures, T comparisonResult, long timeoutInMillis) throws InterruptedException, TimeoutException { if (futures == null) { return 0; } int resultCount = 0; Iterator<? extends Future<?>> it = futures.iterator(); long startTime = Clock.accurateForwardProgressingMillis(); long remainingTime; while (it.hasNext() && (remainingTime = timeoutInMillis - (Clock.lastKnownForwardProgressingMillis() - startTime)) > 0) { Future<?> f = it.next(); try { if (comparisonResult == null) { if (f.get(remainingTime, TimeUnit.MILLISECONDS) == null) { resultCount++; } } else if (comparisonResult.equals(f.get(remainingTime, TimeUnit.MILLISECONDS))) { resultCount++; } } catch (CancellationException e) { // swallowed } catch (ExecutionException e) { // swallowed } } if (it.hasNext()) { throw new TimeoutException(); } return resultCount; } /** * An alternative to {@link #blockTillAllComplete(Iterable)}, this provides the ability to know * when all futures are complete without blocking. Unlike * {@link #blockTillAllComplete(Iterable)}, this requires that you provide a collection of * {@link ListenableFuture}'s. But will return immediately, providing a new * {@link ListenableFuture} that will be called once all the provided futures have finished. * <p> * The future returned will provide a {@code null} result, it is the responsibility of the * caller to get the actual results from the provided futures. This is designed to just be an * indicator as to when they have finished. If you need the results from the provided futures, * consider using {@link #makeCompleteListFuture(Iterable)}. You should also consider using * {@link #makeFailurePropagatingCompleteFuture(Iterable)}, it has the same semantics as this one * except it will put the returned future into an error state if any of the provided futures error. * * @since 1.2.0 * @param futures Collection of futures that must finish before returned future is satisfied * @return ListenableFuture which will be done once all futures provided are done */ public static ListenableFuture<?> makeCompleteFuture(Iterable<? extends ListenableFuture<?>> futures) { return new EmptyFutureCollection(futures); } /** * An alternative to {@link #blockTillAllComplete(Iterable)}, this provides the ability to know * when all futures are complete without blocking. Unlike * {@link #blockTillAllComplete(Iterable)}, this requires that you provide a collection of * {@link ListenableFuture}'s. But will return immediately, providing a new * {@link ListenableFuture} that will be called once all the provided futures have finished. * <p> * The future returned will provide the result object once all provided futures have completed. * If any failures occured, they will not be represented in the returned future. If that is * desired you should consider using * {@link #makeFailurePropagatingCompleteFuture(Iterable, Object)}, it has the same semantics as * this one except it will put the returned future into an error state if any of the provided * futures error. * * @since 3.3.0 * @param <T> type of result returned from the future * @param futures Collection of futures that must finish before returned future is satisfied * @param result Result to provide returned future once all futures complete * @return ListenableFuture which will be done once all futures provided are done */ public static <T> ListenableFuture<T> makeCompleteFuture(Iterable<? extends ListenableFuture<?>> futures, final T result) { // TODO - evaluate changing to return new EmptyFutureCollection(futures).map((ignored) -> result); final EmptyFutureCollection efc = new EmptyFutureCollection(futures); final SettableListenableFuture<T> resultFuture = new CancelDelegateSettableListenableFuture<>(efc); efc.addCallback(new FutureCallback<Object>() { @Override public void handleResult(Object ignored) { resultFuture.setResult(result); } @Override public void handleFailure(Throwable t) { if (t instanceof CancellationException) { // caused by user canceling returned CancelDelegateSettableListenableFuture } else { resultFuture.setFailure(t); } } }); return resultFuture; } /** * Similar to {@link #makeCompleteFuture(Iterable)} in that the returned future wont complete * until all the provided futures complete. However this implementation will check if any * futures failed or were canceled once all have completed. If any did not complete normally * then the returned futures state will match the state of one of the futures that did not * normally (randomly chosen). * <p> * Since the returned future wont complete until all futures complete, you may want to consider * using {@link #cancelIncompleteFuturesIfAnyFail(boolean, Iterable, boolean)} in addition to * this so that the future will resolve as soon as any failures occur. * * @since 5.0.0 * @param futures Collection of futures that must finish before returned future is satisfied * @return ListenableFuture which will be done once all futures provided are done */ public static ListenableFuture<?> makeFailurePropagatingCompleteFuture(Iterable<? extends ListenableFuture<?>> futures) { return makeFailurePropagatingCompleteFuture(futures, null); } /** * Similar to {@link #makeCompleteFuture(Iterable, Object)} in that the returned future wont * complete until all the provided futures complete. However this implementation will check if * any futures failed or were canceled once all have completed. If any did not complete normally * then the returned futures state will match the state of one of the futures that did not * normally (randomly chosen). * <p> * Since the returned future wont complete until all futures complete, you may want to consider * using {@link #cancelIncompleteFuturesIfAnyFail(boolean, Iterable, boolean)} in addition to * this so that the future will resolve as soon as any failures occur. * * @since 5.0.0 * @param <T> type of result returned from the future * @param futures Collection of futures that must finish before returned future is satisfied * @param result Result to provide returned future once all futures complete * @return ListenableFuture which will be done once all futures provided are done */ public static <T> ListenableFuture<T> makeFailurePropagatingCompleteFuture(Iterable<? extends ListenableFuture<?>> futures, final T result) { FailureFutureCollection<Object> ffc = new FailureFutureCollection<>(futures); final CancelDelegateSettableListenableFuture<T> resultFuture = new CancelDelegateSettableListenableFuture<>(ffc); ffc.addCallback(new FutureCallback<List<ListenableFuture<?>>>() { @Override public void handleResult(List<ListenableFuture<?>> failedFutures) { if (failedFutures.isEmpty()) { resultFuture.setResult(result); } else { // propagate error ListenableFuture<?> f = failedFutures.get(0); if (f.isCancelled()) { resultFuture.cancelRegardlessOfDelegateFutureState(); } else { try { f.get(); } catch (ExecutionException e) { resultFuture.setFailure(e.getCause()); } catch (InterruptedException e) { // should not be possible throw new RuntimeException(e); } } } } @Override public void handleFailure(Throwable t) { if (t instanceof CancellationException) { // caused by user canceling returned CancelDelegateSettableListenableFuture } else { resultFuture.setFailure(t); } } }); return resultFuture; } /** * This call is similar to {@link #makeCompleteFuture(Iterable)} in that it will immediately * provide a future that will not be satisfied till all provided futures complete. * <p> * This future provides a list of the completed futures as the result. The order of this list * is NOT deterministic. * <p> * If {@link ListenableFuture#cancel(boolean)} is invoked on the returned future, all provided * futures will attempt to be canceled in the same way. * * @since 1.2.0 * @param <T> The result object type returned from the futures * @param futures Structure of futures to iterate over * @return ListenableFuture which will be done once all futures provided are done */ public static <T> ListenableFuture<List<ListenableFuture<? extends T>>> makeCompleteListFuture(Iterable<? extends ListenableFuture<? extends T>> futures) { return new AllFutureCollection<>(futures); } /** * This call is similar to {@link #makeCompleteFuture(Iterable)} in that it will immediately * provide a future that will not be satisfied till all provided futures complete. * <p> * This future provides a list of the futures that completed without throwing an exception nor * were canceled. The order of the resulting list is NOT deterministic. * <p> * If {@link ListenableFuture#cancel(boolean)} is invoked on the returned future, all provided * futures will attempt to be canceled in the same way. * * @since 1.2.0 * @param <T> The result object type returned from the futures * @param futures Structure of futures to iterate over * @return ListenableFuture which will be done once all futures provided are done */ public static <T> ListenableFuture<List<ListenableFuture<? extends T>>> makeSuccessListFuture(Iterable<? extends ListenableFuture<? extends T>> futures) { return new SuccessFutureCollection<>(futures); } /** * This call is similar to {@link #makeCompleteFuture(Iterable)} in that it will immediately * provide a future that will not be satisfied till all provided futures complete. * <p> * This future provides a list of the futures that failed by either throwing an exception or * were canceled. The order of the resulting list is NOT deterministic. * <p> * If {@link ListenableFuture#cancel(boolean)} is invoked on the returned future, all provided * futures will attempt to be canceled in the same way. * * @since 1.2.0 * @param <T> The result object type returned from the futures * @param futures Structure of futures to iterate over * @return ListenableFuture which will be done once all futures provided are done */ public static <T> ListenableFuture<List<ListenableFuture<? extends T>>> makeFailureListFuture(Iterable<? extends ListenableFuture<? extends T>> futures) { return new FailureFutureCollection<>(futures); } /** * This returns a future which provides the results of all the provided futures. Thus * preventing the need to iterate over all the futures and manually extract the results. This * call does NOT block, instead it will return a future which will not complete until all the * provided futures complete. * <p> * The order of the result list is NOT deterministic. * <p> * If called with {@code true} for {@code ignoreFailedFutures}, even if some of the provided * futures finished in error, they will be ignored and just the successful results will be * provided. If called with {@code false} then if any futures complete in error, then the * returned future will throw a {@link ExecutionException} with the error as the cause when * {@link Future#get()} is invoked. In addition if called with {@code false} and any of the * provided futures are canceled, then the returned future will also be canceled, resulting in a * {@link CancellationException} being thrown when {@link Future#get()} is invoked. In the case * where there is canceled and failed exceptions in the collection, this will prefer to throw the * failure as an {@link ExecutionException} rather than obscure it with a * {@link CancellationException}. In other words {@link CancellationException} will be thrown * ONLY if there was canceled tasks, but NO tasks which finished in error. * * @since 4.0.0 * @param <T> The result object type returned from the futures * @param futures Structure of futures to iterate over and extract results from * @param ignoreFailedFutures {@code true} to ignore any failed or canceled futures * @return A {@link ListenableFuture} which will provide a list of the results from the provided futures */ public static <T> ListenableFuture<List<T>> makeResultListFuture(Iterable<? extends ListenableFuture<? extends T>> futures, final boolean ignoreFailedFutures) { if (futures == null) { return immediateResultFuture(Collections.<T>emptyList()); } ListenableFuture<List<ListenableFuture<? extends T>>> completeFuture = makeCompleteListFuture(futures); final SettableListenableFuture<List<T>> result; result = new CancelDelegateSettableListenableFuture<>(completeFuture); completeFuture.addCallback(new FutureCallback<List<ListenableFuture<? extends T>>>() { @Override public void handleResult(List<ListenableFuture<? extends T>> resultFutures) { boolean needToCancel = false; ArrayList<T> results = new ArrayList<>(resultFutures.size()); Iterator<ListenableFuture<? extends T>> it = resultFutures.iterator(); while (it.hasNext()) { ListenableFuture<? extends T> f = it.next(); if (f.isCancelled()) { if (! ignoreFailedFutures) { needToCancel = true; // mark to cancel, but search for failure before actually canceling } continue; } try { results.add(f.get()); } catch (ExecutionException e) { if (! ignoreFailedFutures) { result.setFailure(e.getCause()); return; } } catch (Exception e) { // should not be possible, future is done, cancel checked first, and ExecutionException caught result.setFailure(new Exception(e)); return; } } if (needToCancel) { if (! result.cancel(false)) { throw new IllegalStateException(); } } else { result.setResult(results); } } @Override public void handleFailure(Throwable t) { if (t instanceof CancellationException) { // caused by user canceling returned CancelDelegateSettableListenableFuture } else { result.setFailure(t); } } }); return result; } /** * Invoked {@link Future#cancel(boolean)} for every future in this collection. Thus if there * are any futures which have not already completed, they will now be marked as canceled. * * @param futures Collection of futures to iterate through and cancel * @param interruptThread Valued passed in to interrupt thread when calling {@link Future#cancel(boolean)} */ public static void cancelIncompleteFutures(Iterable<? extends Future<?>> futures, boolean interruptThread) { if (futures == null) { return; } for (Future<?> f : futures) { f.cancel(interruptThread); } } /** * Provide a group of futures and cancel all of them if any of them are canceled or fail. * <p> * If {@code false} is provided for {@code copy} parameter, then {@code futures} will be * iterated over twice, once during this invocation, and again when needing to cancel the * futures. Because of that it is critical the {@link Iterable} provided returns the exact same * future contents at the time of invoking this call. If that guarantee can not be provided, * you must specify {@code true} for the {@code copy} parameter. * * @since 4.7.2 * @param copy {@code true} to copy provided futures to avoid * @param futures Futures to be monitored and canceled on error * @param interruptThread Valued passed in to interrupt thread when calling {@link Future#cancel(boolean)} */ public static void cancelIncompleteFuturesIfAnyFail(boolean copy, Iterable<? extends ListenableFuture<?>> futures, final boolean interruptThread) { if (futures == null) { return; } final ArrayList<ListenableFuture<?>> futuresCopy; final Iterable<? extends ListenableFuture<?>> callbackFutures; if (copy) { callbackFutures = futuresCopy = new ArrayList<>(); } else { futuresCopy = null; callbackFutures = futures; } CancelOnErrorFutureCallback cancelingCallback = new CancelOnErrorFutureCallback(callbackFutures, interruptThread); for (ListenableFuture<?> f : futures) { if (copy) { futuresCopy.add(f); } f.addCallback(cancelingCallback); } } /** * Constructs a {@link ListenableFuture} that has already had the provided result given to it. * Thus the resulting future can not error, block, or be canceled. * <p> * If {@code null} is provided here the static instance of * {@link ImmediateResultListenableFuture#NULL_RESULT} will be returned to reduce GC overhead. * * @since 1.2.0 * @param <T> The result object type returned by the returned future * @param result result to be provided in .get() call * @return Already satisfied future */ @SuppressWarnings("unchecked") public static <T> ListenableFuture<T> immediateResultFuture(T result) { if (result == null) { return (ListenableFuture<T>)ImmediateResultListenableFuture.NULL_RESULT; } else { return new ImmediateResultListenableFuture<>(result); } } /** * Constructs a {@link ListenableFuture} that has failed with the given failure. Thus the * resulting future can not block, or be canceled. Calls to {@link ListenableFuture#get()} will * immediately throw an {@link ExecutionException}. * * @since 1.2.0 * @param <T> The result object type returned by the returned future * @param failure to provide as cause for ExecutionException thrown from .get() call * @return Already satisfied future */ public static <T> ListenableFuture<T> immediateFailureFuture(Throwable failure) { return new ImmediateFailureListenableFuture<>(failure); } /** * Will continue to schedule the provided task as long as the task is returning a {@code null} * result. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only complete with a result once the provided task returns a * non-null result. Canceling the returned future will prevent future executions from being * attempted. Canceling with an interrupt will transmit the interrupt to the running task if it * is currently running. * <p> * The first execution will either be immediately executed in thread or submitted for immediate * execution on the provided scheduler (depending on {@code firstRunAsync} parameter). Once * this execution completes, if the result is {@code null} then the task will be rescheduled for * execution. If non-null then the result will be able to be retrieved from the returned * {@link ListenableFuture}. * <p> * If you want to ensure this does not reschedule forever consider using * {@link #scheduleWhileTaskResultNull(SubmitterScheduler, long, boolean, Callable, long)}. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay in milliseconds to schedule out future attempts * @param firstRunAsync {@code False} to run first try on invoking thread, {@code true} to submit on scheduler * @param task Task which will provide result, or {@code null} to reschedule itself again * @return Future that will resolve with non-null result from task */ public static <T> ListenableFuture<T> scheduleWhileTaskResultNull(SubmitterScheduler scheduler, long scheduleDelayMillis, boolean firstRunAsync, Callable<? extends T> task) { return scheduleWhileTaskResultNull(scheduler, scheduleDelayMillis, firstRunAsync, task, -1); } /** * Will continue to schedule the provided task as long as the task is returning a {@code null} * result. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only complete with a result once the provided task returns a non-null * result, or until the provided timeout is reached. If the timeout is reached then the task * wont be rescheduled. Instead the future will be resolved with a {@code null} result. Even if * only 1 millisecond before timeout, the entire rescheduleDelayMillis will be provided for the * next attempt's scheduled delay. Canceling the returned future will prevent future executions * from being attempted. Canceling with an interrupt will transmit the interrupt to the running * task if it is currently running. * <p> * The first execution will either be immediately executed in thread or submitted for immediate * execution on the provided scheduler (depending on {@code firstRunAsync} parameter). Once * this execution completes, if the result is {@code null} then the task will be rescheduled for * execution. If non-null then the result will be able to be retrieved from the returned * {@link ListenableFuture}. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay in milliseconds to schedule out future attempts * @param firstRunAsync {@code False} to run first try on invoking thread, {@code true} to submit on scheduler * @param task Task which will provide result, or {@code null} to reschedule itself again * @param timeoutMillis Timeout in milliseconds task wont be rescheduled and instead just finish with {@code null} * @return Future that will resolve with non-null result from task */ public static <T> ListenableFuture<T> scheduleWhileTaskResultNull(SubmitterScheduler scheduler, long scheduleDelayMillis, boolean firstRunAsync, Callable<? extends T> task, long timeoutMillis) { return scheduleWhile(scheduler, scheduleDelayMillis, firstRunAsync, task, (r) -> r == null, timeoutMillis); } /** * Executes a task, checking the result from the task to see if it needs to reschedule the task * again. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only provide a result once the looping of the task has completed. * Canceling the returned future will prevent future executions from being attempted. Canceling * with an interrupt will transmit the interrupt to the running task if it is currently running. * <p> * The first execution will either be immediately executed in thread or submitted for * immediate execution on the provided scheduler (depending on {@code firstRunAsync} parameter). * Once this execution completes the result will be provided to the {@link Predicate} to * determine if another schedule should occur to re-run the task. * <p> * If you want to ensure this does not reschedule forever consider using * {@link #scheduleWhile(SubmitterScheduler, long, boolean, Callable, Predicate, long)}. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay after predicate indicating to loop again before re-executed * @param firstRunAsync {@code False} to run first try on invoking thread, {@code true} to submit on scheduler * @param task Task which will provide result to compare in provided {@code Predicate} * @param loopTest Test for result to see if scheduled loop should continue * @return Future that will resolve once returned {@link Predicate} returns {@code false} */ public static <T> ListenableFuture<T> scheduleWhile(SubmitterScheduler scheduler, long scheduleDelayMillis, boolean firstRunAsync, Callable<? extends T> task, Predicate<? super T> loopTest) { return scheduleWhile(scheduler, scheduleDelayMillis, firstRunAsync, task, loopTest, -1); } /** * Executes a task, checking the result from the task to see if it needs to reschedule the task * again. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only provide a result once the looping of the task has completed, or * until the provided timeout is reached. If the timeout is reached then the task wont be * rescheduled. Instead the future will resolve with the last known task result. Even if only * 1 millisecond before timeout, the entire rescheduleDelayMillis will be provided for the next * attempt's scheduled delay. Canceling the returned future will prevent future executions from * being attempted. Canceling with an interrupt will transmit the interrupt to the running task * if it is currently running. * <p> * The first execution will either be immediately executed in thread or submitted for immediate * execution on the provided scheduler (depending on {@code firstRunAsync} parameter). Once * this execution completes the result will be provided to the {@link Predicate} to determine if * another schedule should occur to re-run the task. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay after predicate indicating to loop again before re-executed * @param firstRunAsync {@code False} to run first try on invoking thread, {@code true} to submit on scheduler * @param task Task which will provide result to compare in provided {@code Predicate} * @param loopTest Test for result to see if scheduled loop should continue * @param timeoutMillis If greater than zero, wont reschedule and instead will just return the last result * @return Future that will resolve once returned {@link Predicate} returns {@code false} or timeout is reached */ public static <T> ListenableFuture<T> scheduleWhile(SubmitterScheduler scheduler, long scheduleDelayMillis, boolean firstRunAsync, Callable<? extends T> task, Predicate<? super T> loopTest, long timeoutMillis) { return scheduleWhile(scheduler, scheduleDelayMillis, firstRunAsync ? scheduler.submit(task) : SameThreadSubmitterExecutor.instance().submit(task), task, loopTest, timeoutMillis); } /** * Executes a task, checking the result from the task to see if it needs to reschedule the task * again. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only provide a result once the looping of the task has completed. * Canceling the returned future will prevent future executions from being attempted. Canceling * with an interrupt will transmit the interrupt to the running task if it is currently running. * <p> * The first execution will happen as soon as the provided {@code startingFuture} completes. * <p> * If you want to ensure this does not reschedule forever consider using * {@link #scheduleWhile(SubmitterScheduler, long, ListenableFuture, Callable, Predicate, long)}. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay after predicate indicating to loop again before re-executed * @param startingFuture Future to use for first result to test for loop * @param task Task which will provide result to compare in provided {@code Predicate} * @param loopTest Test for result to see if scheduled loop should continue * @return Future that will resolve once returned {@link Predicate} returns {@code false} */ public static <T> ListenableFuture<T> scheduleWhile(SubmitterScheduler scheduler, long scheduleDelayMillis, ListenableFuture<? extends T> startingFuture, Callable<? extends T> task, Predicate<? super T> loopTest) { return scheduleWhile(scheduler, scheduleDelayMillis, startingFuture, task, loopTest, -1); } /** * Executes a task, checking the result from the task to see if it needs to reschedule the task * again. This can be a good way to implement retry logic where a result ultimately needs to be * communicated through a future. * <p> * The returned future will only provide a result once the looping of the task has completed, or * until the provided timeout is reached. If the timeout is reached then the task wont be * rescheduled. Instead the future will resolve with the last known task result. Even if only * 1 millisecond before timeout, the entire rescheduleDelayMillis will be provided for the next * attempt's scheduled delay. Canceling the returned future will prevent future executions from * being attempted. Canceling with an interrupt will transmit the interrupt to the running task * if it is currently running. * <p> * The first execution will happen as soon as the provided {@code startingFuture} completes. * * @since 5.0.0 * @param <T> The result object type returned by the task and provided by the future * @param scheduler Scheduler to schedule out task executions * @param scheduleDelayMillis Delay after predicate indicating to loop again before re-executed * @param startingFuture Future to use for first result to test for loop * @param task Task which will provide result to compare in provided {@code Predicate} * @param loopTest Test for result to see if scheduled loop should continue * @param timeoutMillis If greater than zero, wont reschedule and instead will just return the last result * @return Future that will resolve once returned {@link Predicate} returns {@code false} */ public static <T> ListenableFuture<T> scheduleWhile(SubmitterScheduler scheduler, long scheduleDelayMillis, ListenableFuture<? extends T> startingFuture, Callable<? extends T> task, Predicate<? super T> loopTest, long timeoutMillis) { ArgumentVerifier.assertNotNull(scheduler, "scheduler"); ArgumentVerifier.assertNotNegative(scheduleDelayMillis, "scheduleDelayMillis"); ArgumentVerifier.assertNotNull(startingFuture, "startingFuture"); ArgumentVerifier.assertNotNull(task, "task"); ArgumentVerifier.assertNotNull(loopTest, "loopTest"); // shortcut optimization in case future is done, and loop is not needed if (startingFuture.isDone()) { if (startingFuture.isCancelled()) { SettableListenableFuture<T> resultFuture = new SettableListenableFuture<>(); resultFuture.cancel(false); return resultFuture; } try { if (! loopTest.test(startingFuture.get())) { return immediateResultFuture(startingFuture.get()); } // else, we need to go through the normal scheduling logic below } catch (ExecutionException e) { return immediateFailureFuture(e.getCause()); } catch (InterruptedException e) { // should not be possible throw new RuntimeException(e); } catch (Throwable t) { return immediateFailureFuture(t); } } long startTime = timeoutMillis > 0 ? Clock.accurateForwardProgressingMillis() : -1; // can not assert not resolved in parallel because user may cancel future at any point SettableListenableFuture<T> resultFuture = new SettableListenableFuture<>(false); Callable<T> cancelCheckingTask = new Callable<T>() { @Override public T call() throws Exception { // set thread before check canceled state so if canceled with interrupt we will interrupt the call resultFuture.setRunningThread(Thread.currentThread()); try { if (! resultFuture.isCancelled()) { return task.call(); } else { // result future is already complete, so throw to avoid executing, but only throw such // that we wont attempt to do anything with the result future throw FailurePropogatingFutureCallback.IGNORED_FAILURE; } } finally { resultFuture.setRunningThread(null); } } }; startingFuture.addCallback(new FailurePropogatingFutureCallback<T>(resultFuture) { @Override public void handleResult(T result) { try { if ((startTime < 0 || (Clock.lastKnownForwardProgressingMillis() - startTime < timeoutMillis)) && loopTest.test(result)) { scheduler.submitScheduled(cancelCheckingTask, scheduleDelayMillis) .addCallback(this); // add this to check again once execution completes } else { // once we have our result, this will end our loop resultFuture.setResult(result); } } catch (Throwable t) { // failure likely from the predicate test resultFuture.setFailure(t); } } }); return resultFuture; } /** * Transform a future's result into another future by applying the provided transformation * function. If the future completed in error, then the mapper will not be invoked, and instead * the returned future will be completed in the same error state this future resulted in. If the * mapper function itself throws an Exception, then the returned future will result in the error * thrown from the mapper. * <p> * This can be easily used to chain together a series of operations, happening async until the * final result is actually needed. Once the future completes the mapper function will be invoked * on the executor (if provided). Because of that providing an executor can ensure this will * never block. If an executor is not provided then the mapper may be invoked on the calling * thread (if the future is already complete), or on the same thread which the future completes * on. If the mapper function is very fast and cheap to run then {@link #map(Function)} or * providing {@code null} for the executor can allow more efficient operation. * * @since 5.0.0 * @param <ST> The source type for the object returned from the future and inputed into the mapper * @param <RT> The result type for the object returned from the mapper * @param sourceFuture Future to source input into transformation function * @param transformer Function to apply result from future into returned future * @param executor Executor to execute transformation function on, or {@code null} * @return Future with result of transformation function or respective error */ protected static <ST, RT> ListenableFuture<RT> transform(ListenableFuture<ST> sourceFuture, Function<? super ST, ? extends RT> transformer, Executor executor) { if (executor == null && sourceFuture.isDone() && ! sourceFuture.isCancelled()) { // optimized path for already complete futures which we can now process in thread try { return FutureUtils.immediateResultFuture(transformer.apply(sourceFuture.get())); } catch (InterruptedException e) { // should not be possible throw new RuntimeException(e); } catch (ExecutionException e) { // failure in getting result from future, transfer failure return FutureUtils.immediateFailureFuture(e.getCause()); } catch (Throwable t) { // failure calculating transformation return FutureUtils.immediateFailureFuture(t); } } else { SettableListenableFuture<RT> slf; if (sourceFuture.isCancelled()) { // shortcut slf = new SettableListenableFuture<>(); slf.cancel(false); return slf; } slf = new CancelDelegateSettableListenableFuture<>(sourceFuture); // may still process in thread if future completed after check and executor is null sourceFuture.addCallback(new FailurePropogatingFutureCallback<ST>(slf) { @Override public void handleResult(ST result) { try { slf.setResult(transformer.apply(result)); } catch (Throwable t) { slf.setFailure(t); } } }, executor); return slf; } } /** * Similar to {@link #transform(ListenableFuture, Function, Executor)} except designed to handle * functions which return futures. This will take what otherwise would be * {@code ListenableFuture<ListenableFuture<R>>}, and flattens it into a single future which will * resolve once the contained future is complete. * * @since 5.0.0 * @param <ST> The source type for the object returned from the future and inputed into the mapper * @param <RT> The result type for the object contained in the future returned from the mapper * @param sourceFuture Future to source input into transformation function * @param transformer Function to apply result from future into returned future * @param executor Executor to execute transformation function on, or {@code null} * @return Future with result of transformation function or respective error */ protected static <ST, RT> ListenableFuture<RT> flatTransform(ListenableFuture<ST> sourceFuture, Function<? super ST, ListenableFuture<RT>> transformer, Executor executor) { if (executor == null && sourceFuture.isDone() && ! sourceFuture.isCancelled()) { // optimized path for already complete futures which we can now process in thread try { return transformer.apply(sourceFuture.get()); } catch (InterruptedException e) { // should not be possible throw new RuntimeException(e); } catch (ExecutionException e) { // failure in getting result from future, transfer failure return FutureUtils.immediateFailureFuture(e.getCause()); } catch (Throwable t) { // failure calculating transformation return FutureUtils.immediateFailureFuture(t); } } else { SettableListenableFuture<RT> slf; if (sourceFuture.isCancelled()) { // shortcut slf = new SettableListenableFuture<>(); slf.cancel(false); return slf; } slf = new CancelDelegateSettableListenableFuture<>(sourceFuture); sourceFuture.addCallback(new FailurePropogatingFutureCallback<ST>(slf) { @Override public void handleResult(ST result) { try { transformer.apply(result) .addCallback(new FailurePropogatingFutureCallback<RT>(slf) { @Override public void handleResult(RT result) { slf.setResult(result); } }); } catch (Throwable t) { slf.setFailure(t); } } }, executor); return slf; } } /** * Class which will propagate a failure condition to a {@link SettableListenableFuture} from a * source future which this is added as a {@link FutureCallback} to. * * @since 5.0.0 * @param <T> Type of result to be accepted by {@link FutureCallback} */ protected abstract static class FailurePropogatingFutureCallback<T> implements FutureCallback<T> { /** * The instance of the only exception which this callback will not propagate. It must be the * exact exception, and can not be hidden inside a cause chain. */ protected static final RuntimeException IGNORED_FAILURE = new SuppressedStackRuntimeException(); private final SettableListenableFuture<?> settableFuture; protected FailurePropogatingFutureCallback(SettableListenableFuture<?> settableFuture) { this.settableFuture = settableFuture; } @Override public void handleFailure(Throwable t) { if (t == IGNORED_FAILURE) { // ignored } else if (t instanceof CancellationException) { settableFuture.cancel(false); } else { settableFuture.setFailure(t); } } } /** * Implementation of {@link SettableListenableFuture} which delegates it's cancel operation to a * parent future. * * @since 4.1.0 * @param <T> The result object type returned from the futures */ protected static class CancelDelegateSettableListenableFuture<T> extends SettableListenableFuture<T> { private final ListenableFuture<?> cancelDelegateFuture; protected CancelDelegateSettableListenableFuture(ListenableFuture<?> lf) { super(false); cancelDelegateFuture = lf; } protected void cancelRegardlessOfDelegateFutureState() { if (super.cancel(false)) { cancelDelegateFuture.cancel(false); } } @Override public boolean cancel(boolean interruptThread) { if (cancelDelegateFuture.cancel(interruptThread)) { return super.cancel(interruptThread); } else { return false; } } } /** * A future implementation that will return a List of futures as the result. The future will * not be satisfied till all provided futures have completed. * * @since 1.2.0 * @param <T> The result object type returned from the futures */ protected abstract static class FutureCollection<T> extends SettableListenableFuture<List<ListenableFuture<? extends T>>> { protected final AtomicInteger remainingResult; private final AtomicReference<ConcurrentArrayList<ListenableFuture<? extends T>>> buildingResult; private ArrayList<ListenableFuture<? extends T>> futures; protected FutureCollection(Iterable<? extends ListenableFuture<? extends T>> source) { super(false); remainingResult = new AtomicInteger(0); // may go negative if results finish before all are added buildingResult = new AtomicReference<>(null); futures = new ArrayList<>(); if (source != null) { Iterator<? extends ListenableFuture<? extends T>> it = source.iterator(); while (it.hasNext()) { ListenableFuture<? extends T> f = it.next(); futures.add(f); attachFutureDoneTask(f); } } futures.trimToSize(); // we need to verify that all futures have not already completed if (remainingResult.addAndGet(futures.size()) == 0) { setResult(getFinalResultList()); } addListener(new Runnable() { @Override public void run() { futures = null; } }); } /** * Attach a {@link FutureDoneTask} to the provided future. This is necessary for tracking as * futures complete, failing to attach a task could result in this future never completing. * <p> * This is provided as a separate function so it can be overriden to provide different * {@link FutureDoneTask} implementation. * * @param f Future to attach to */ protected void attachFutureDoneTask(ListenableFuture<? extends T> f) { f.addListener(new FutureDoneTask(f)); } @Override public boolean cancel(boolean interrupt) { // we need a copy in case canceling clears out the futures ArrayList<ListenableFuture<? extends T>> futures = this.futures; if (super.cancel(interrupt)) { cancelIncompleteFutures(futures, interrupt); return true; } else { return false; } } /** * Adds item to the result list. This list may be lazily constructed and thus why you must add * through this function rather than directly on to the list. */ protected void addResult(ListenableFuture<? extends T> f) { List<ListenableFuture<? extends T>> list = buildingResult.get(); if (list == null) { int rearPadding = remainingResult.get(); if (rearPadding < 0) { rearPadding *= -1; } ConcurrentArrayList<ListenableFuture<? extends T>> newList = new ConcurrentArrayList<>(0, rearPadding); if (buildingResult.compareAndSet(null, newList)) { list = newList; list.add(f); // must add before updating the rear padding if (rearPadding > 2) { // set back to reasonable number after construction in hopes that we wont have to expand much newList.setRearPadding(2); } return; // return so we don't add again } else { list = buildingResult.get(); } } list.add(f); } /** * Gives the implementing class the option to save or check the completed future. * * @param f {@link ListenableFuture} that has completed */ protected abstract void handleFutureDone(ListenableFuture<? extends T> f); /** * Will only be called once, and all allocated resources can be freed after this point. * * @return List to satisfy ListenableFuture result with */ protected List<ListenableFuture<? extends T>> getFinalResultList() { ConcurrentArrayList<ListenableFuture<? extends T>> resultsList = buildingResult.get(); if (resultsList == null) { return Collections.emptyList(); } else { buildingResult.lazySet(null); resultsList.trimToSize(); return Collections.unmodifiableList(resultsList); } } /** * Task which is ran after a future completes. This is used internally to track how many * outstanding tasks are remaining, as well as used to collect the results if desired. * * @since 4.7.0 */ protected class FutureDoneTask implements Runnable { private final ListenableFuture<? extends T> f; protected FutureDoneTask(ListenableFuture<? extends T> f) { this.f = f; } @Override public void run() { try { // exceptions should not be possible, but done for robustness handleFutureDone(f); } finally { // all futures are now done if (remainingResult.decrementAndGet() == 0) { setResult(getFinalResultList()); } } } } } /** * A future implementation that will be satisfied till all provided futures have completed. * * @since 1.2.0 */ protected static class EmptyFutureCollection extends FutureCollection<Object> { private FutureDoneTask doneTaskSingleton = null; protected EmptyFutureCollection(Iterable<? extends ListenableFuture<?>> source) { super(source); } @Override protected void handleFutureDone(ListenableFuture<?> f) { // ignored } @Override protected List<ListenableFuture<?>> getFinalResultList() { return null; } @Override protected void attachFutureDoneTask(ListenableFuture<?> f) { // we don't care about the result of the future // so to save a little memory we reuse the same task with no future provided if (doneTaskSingleton == null) { doneTaskSingleton = new FutureDoneTask(null); } f.addListener(doneTaskSingleton); } } /** * A future implementation that will return a List of futures as the result. The future will * not be satisfied till all provided futures have completed. * <p> * This implementation will return a result of all the futures that completed. * * @since 1.2.0 * @param <T> The result object type returned from the futures */ protected static class AllFutureCollection<T> extends FutureCollection<T> { protected AllFutureCollection(Iterable<? extends ListenableFuture<? extends T>> source) { super(source); } @Override protected void handleFutureDone(ListenableFuture<? extends T> f) { addResult(f); } } /** * A future implementation that will return a List of futures as the result. The future will * not be satisfied till all provided futures have completed. * <p> * This implementation will return a result of all the futures that completed successfully. * If the future was canceled or threw an exception it will not be included. * * @since 1.2.0 * @param <T> The result object type returned from the futures */ protected static class SuccessFutureCollection<T> extends AllFutureCollection<T> { protected SuccessFutureCollection(Iterable<? extends ListenableFuture<? extends T>> source) { super(source); } @Override protected void handleFutureDone(ListenableFuture<? extends T> f) { if (f.isCancelled()) { // detect canceled conditions before an exception would have otherwise thrown // canceled futures are ignored return; } try { f.get(); // if no exception thrown, add future super.handleFutureDone(f); } catch (InterruptedException e) { /* should not be possible since this should only * be called once the future is already done */ Thread.currentThread().interrupt(); } catch (ExecutionException e) { // ignored } catch (CancellationException e) { // should not be possible due check at start on what should be an already done future throw e; } } } /** * future implementation that will return a List of futures as the result. The future will * not be satisfied till all provided futures have completed. * <p> * This implementation will return a result of all the futures that either threw an exception * during computation, or was canceled. * * @since 1.2.0 * @param <T> The result object type returned from the futures */ protected static class FailureFutureCollection<T> extends AllFutureCollection<T> { protected FailureFutureCollection(Iterable<? extends ListenableFuture<? extends T>> source) { super(source); } @Override protected void handleFutureDone(ListenableFuture<? extends T> f) { if (f.isCancelled()) { // detect canceled conditions before an exception would have otherwise thrown super.handleFutureDone(f); return; } try { f.get(); } catch (InterruptedException e) { /* should not be possible since this should only * be called once the future is already done */ Thread.currentThread().interrupt(); } catch (ExecutionException e) { // failed so add it super.handleFutureDone(f); } catch (CancellationException e) { // should not be possible due check at start on what should be an already done future throw e; } } } /** * Future callback that on error condition will cancel all the provided futures. * * @since 4.7.2 */ protected static class CancelOnErrorFutureCallback extends AbstractFutureCallbackFailureHandler { private final Iterable<? extends ListenableFuture<?>> futures; private final boolean interruptThread; private final AtomicBoolean canceled; public CancelOnErrorFutureCallback(Iterable<? extends ListenableFuture<?>> futures, boolean interruptThread) { this.futures = futures; this.interruptThread = interruptThread; this.canceled = new AtomicBoolean(false); } @Override public void handleFailure(Throwable t) { if (! canceled.get() && canceled.compareAndSet(false, true)) { cancelIncompleteFutures(futures, interruptThread); } } } }