1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2018 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.producers.internal; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.common.util.concurrent.MoreExecutors.directExecutor; 20*f585d8a3SJacky Wang 21*f585d8a3SJacky Wang import com.google.common.collect.MapMaker; 22*f585d8a3SJacky Wang import com.google.common.util.concurrent.Futures; 23*f585d8a3SJacky Wang import com.google.common.util.concurrent.ListenableFuture; 24*f585d8a3SJacky Wang import dagger.producers.Producer; 25*f585d8a3SJacky Wang import java.util.Collections; 26*f585d8a3SJacky Wang import java.util.Set; 27*f585d8a3SJacky Wang 28*f585d8a3SJacky Wang /** 29*f585d8a3SJacky Wang * Abstract class for implementing producers derived from methods on component dependencies. 30*f585d8a3SJacky Wang * 31*f585d8a3SJacky Wang * <p>Unlike most other {@link CancellableProducer} implementations, cancelling the future returned 32*f585d8a3SJacky Wang * by a {@linkplain #newDependencyView dependency view} injected into an {@code @Produces} method 33*f585d8a3SJacky Wang * will actually cancel the underlying future. This is because the future comes from outside the 34*f585d8a3SJacky Wang * component's producer graph (including possibly from another object that isn't a component at 35*f585d8a3SJacky Wang * all), so if we don't cancel it when the user asks to cancel it, there might just be no way to 36*f585d8a3SJacky Wang * cancel it at all. 37*f585d8a3SJacky Wang */ 38*f585d8a3SJacky Wang public abstract class DependencyMethodProducer<T> implements CancellableProducer<T> { 39*f585d8a3SJacky Wang 40*f585d8a3SJacky Wang /** Weak set of all incomplete futures this producer has returned. */ 41*f585d8a3SJacky Wang private final Set<ListenableFuture<T>> futures = 42*f585d8a3SJacky Wang Collections.newSetFromMap(new MapMaker().weakKeys().<ListenableFuture<T>, Boolean>makeMap()); 43*f585d8a3SJacky Wang 44*f585d8a3SJacky Wang private boolean cancelled = false; 45*f585d8a3SJacky Wang 46*f585d8a3SJacky Wang /** Calls a method on a component dependency to get a future. */ callDependencyMethod()47*f585d8a3SJacky Wang protected abstract ListenableFuture<T> callDependencyMethod(); 48*f585d8a3SJacky Wang 49*f585d8a3SJacky Wang @Override get()50*f585d8a3SJacky Wang public final ListenableFuture<T> get() { 51*f585d8a3SJacky Wang synchronized (futures) { 52*f585d8a3SJacky Wang if (cancelled) { 53*f585d8a3SJacky Wang return Futures.immediateCancelledFuture(); 54*f585d8a3SJacky Wang } 55*f585d8a3SJacky Wang 56*f585d8a3SJacky Wang final ListenableFuture<T> future = callDependencyMethod(); 57*f585d8a3SJacky Wang if (!future.isDone() && futures.add(future)) { 58*f585d8a3SJacky Wang future.addListener( 59*f585d8a3SJacky Wang new Runnable() { 60*f585d8a3SJacky Wang @Override 61*f585d8a3SJacky Wang public void run() { 62*f585d8a3SJacky Wang synchronized (futures) { 63*f585d8a3SJacky Wang futures.remove(future); 64*f585d8a3SJacky Wang } 65*f585d8a3SJacky Wang } 66*f585d8a3SJacky Wang }, 67*f585d8a3SJacky Wang directExecutor()); 68*f585d8a3SJacky Wang } 69*f585d8a3SJacky Wang return future; 70*f585d8a3SJacky Wang } 71*f585d8a3SJacky Wang } 72*f585d8a3SJacky Wang 73*f585d8a3SJacky Wang @Override cancel(boolean mayInterruptIfRunning)74*f585d8a3SJacky Wang public final void cancel(boolean mayInterruptIfRunning) { 75*f585d8a3SJacky Wang synchronized (futures) { 76*f585d8a3SJacky Wang cancelled = true; 77*f585d8a3SJacky Wang for (ListenableFuture<T> future : futures) { 78*f585d8a3SJacky Wang // futures is a concurrent set so that the concurrent removal that will happen here is not 79*f585d8a3SJacky Wang // a problem 80*f585d8a3SJacky Wang future.cancel(mayInterruptIfRunning); 81*f585d8a3SJacky Wang } 82*f585d8a3SJacky Wang } 83*f585d8a3SJacky Wang } 84*f585d8a3SJacky Wang 85*f585d8a3SJacky Wang @Override newDependencyView()86*f585d8a3SJacky Wang public final Producer<T> newDependencyView() { 87*f585d8a3SJacky Wang return this; 88*f585d8a3SJacky Wang } 89*f585d8a3SJacky Wang 90*f585d8a3SJacky Wang @Override newEntryPointView(final CancellationListener cancellationListener)91*f585d8a3SJacky Wang public final Producer<T> newEntryPointView(final CancellationListener cancellationListener) { 92*f585d8a3SJacky Wang return new Producer<T>() { 93*f585d8a3SJacky Wang private final Set<ListenableFuture<T>> entryPointFutures = 94*f585d8a3SJacky Wang Collections.newSetFromMap( 95*f585d8a3SJacky Wang new MapMaker().weakKeys().<ListenableFuture<T>, Boolean>makeMap()); 96*f585d8a3SJacky Wang 97*f585d8a3SJacky Wang @Override 98*f585d8a3SJacky Wang public ListenableFuture<T> get() { 99*f585d8a3SJacky Wang final ListenableFuture<T> future = DependencyMethodProducer.this.get(); 100*f585d8a3SJacky Wang if (!future.isDone() && entryPointFutures.add(future)) { 101*f585d8a3SJacky Wang future.addListener( 102*f585d8a3SJacky Wang new Runnable() { 103*f585d8a3SJacky Wang @Override 104*f585d8a3SJacky Wang public void run() { 105*f585d8a3SJacky Wang entryPointFutures.remove(future); 106*f585d8a3SJacky Wang if (future.isCancelled()) { 107*f585d8a3SJacky Wang // TODO(cgdecker): Make this also propagate the actual value that was passed for 108*f585d8a3SJacky Wang // mayInterruptIfRunning 109*f585d8a3SJacky Wang cancellationListener.onProducerFutureCancelled(true); 110*f585d8a3SJacky Wang } 111*f585d8a3SJacky Wang } 112*f585d8a3SJacky Wang }, 113*f585d8a3SJacky Wang directExecutor()); 114*f585d8a3SJacky Wang } 115*f585d8a3SJacky Wang return future; 116*f585d8a3SJacky Wang } 117*f585d8a3SJacky Wang }; 118*f585d8a3SJacky Wang } 119*f585d8a3SJacky Wang } 120