/* * Copyright (C) 2015 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package com.google.common.util.concurrent; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import com.google.common.annotations.GwtCompatible; import com.google.common.collect.ImmutableCollection; import com.google.j2objc.annotations.WeakOuter; import java.util.concurrent.Callable; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.RejectedExecutionException; import javax.annotation.Nullable; /** * Aggregate future that computes its value by calling a callable. */ @GwtCompatible final class CombinedFuture<V> extends AggregateFuture<Object, V> { CombinedFuture( ImmutableCollection<? extends ListenableFuture<?>> futures, boolean allMustSucceed, Executor listenerExecutor, AsyncCallable<V> callable) { init( new CombinedFutureRunningState( futures, allMustSucceed, new AsyncCallableInterruptibleTask(callable, listenerExecutor))); } CombinedFuture( ImmutableCollection<? extends ListenableFuture<?>> futures, boolean allMustSucceed, Executor listenerExecutor, Callable<V> callable) { init( new CombinedFutureRunningState( futures, allMustSucceed, new CallableInterruptibleTask(callable, listenerExecutor))); } private final class CombinedFutureRunningState extends RunningState { private CombinedFutureInterruptibleTask task; CombinedFutureRunningState( ImmutableCollection<? extends ListenableFuture<? extends Object>> futures, boolean allMustSucceed, CombinedFutureInterruptibleTask task) { super(futures, allMustSucceed, false); this.task = task; } @Override void collectOneValue(boolean allMustSucceed, int index, @Nullable Object returnValue) {} @Override void handleAllCompleted() { CombinedFutureInterruptibleTask localTask = task; if (localTask != null) { localTask.execute(); } else { checkState(isDone()); } } @Override void releaseResourcesAfterFailure() { super.releaseResourcesAfterFailure(); this.task = null; } @Override void interruptTask() { CombinedFutureInterruptibleTask localTask = task; if (localTask != null) { localTask.interruptTask(); } } } @WeakOuter private abstract class CombinedFutureInterruptibleTask extends InterruptibleTask { private final Executor listenerExecutor; volatile boolean thrownByExecute = true; public CombinedFutureInterruptibleTask(Executor listenerExecutor) { this.listenerExecutor = checkNotNull(listenerExecutor); } @Override final void runInterruptibly() { thrownByExecute = false; // Ensure we haven't been cancelled or already run. if (!isDone()) { try { setValue(); } catch (ExecutionException e) { setException(e.getCause()); } catch (CancellationException e) { cancel(false); } catch (Throwable e) { setException(e); } } } @Override final boolean wasInterrupted() { return CombinedFuture.this.wasInterrupted(); } final void execute() { try { listenerExecutor.execute(this); } catch (RejectedExecutionException e) { if (thrownByExecute) { setException(e); } } } abstract void setValue() throws Exception; } @WeakOuter private final class AsyncCallableInterruptibleTask extends CombinedFutureInterruptibleTask { private final AsyncCallable<V> callable; public AsyncCallableInterruptibleTask(AsyncCallable<V> callable, Executor listenerExecutor) { super(listenerExecutor); this.callable = checkNotNull(callable); } @Override void setValue() throws Exception { setFuture(callable.call()); } } @WeakOuter private final class CallableInterruptibleTask extends CombinedFutureInterruptibleTask { private final Callable<V> callable; public CallableInterruptibleTask(Callable<V> callable, Executor listenerExecutor) { super(listenerExecutor); this.callable = checkNotNull(callable); } @Override void setValue() throws Exception { set(callable.call()); } } }