1 /* 2 * Copyright (C) 2016 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.internal; 18 19 import static dagger.internal.Preconditions.checkNotNull; 20 21 import dagger.Lazy; 22 import javax.inject.Provider; 23 24 /** 25 * A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a 26 * delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>. 27 */ 28 public final class DoubleCheck<T> implements Provider<T>, Lazy<T> { 29 private static final Object UNINITIALIZED = new Object(); 30 31 private volatile Provider<T> provider; 32 private volatile Object instance = UNINITIALIZED; 33 DoubleCheck(Provider<T> provider)34 private DoubleCheck(Provider<T> provider) { 35 assert provider != null; 36 this.provider = provider; 37 } 38 39 @SuppressWarnings("unchecked") // cast only happens when result comes from the provider 40 @Override get()41 public T get() { 42 Object result = instance; 43 if (result == UNINITIALIZED) { 44 synchronized (this) { 45 result = instance; 46 if (result == UNINITIALIZED) { 47 result = provider.get(); 48 instance = reentrantCheck(instance, result); 49 /* Null out the reference to the provider. We are never going to need it again, so we 50 * can make it eligible for GC. */ 51 provider = null; 52 } 53 } 54 } 55 return (T) result; 56 } 57 58 /** 59 * Checks to see if creating the new instance has resulted in a recursive call. If it has, and the 60 * new instance is the same as the current instance, return the instance. However, if the new 61 * instance differs from the current instance, an {@link IllegalStateException} is thrown. 62 */ reentrantCheck(Object currentInstance, Object newInstance)63 public static Object reentrantCheck(Object currentInstance, Object newInstance) { 64 boolean isReentrant = !(currentInstance == UNINITIALIZED 65 // This check is needed for fastInit's implementation, which uses MemoizedSentinel types. 66 || currentInstance instanceof MemoizedSentinel); 67 68 if (isReentrant && currentInstance != newInstance) { 69 throw new IllegalStateException("Scoped provider was invoked recursively returning " 70 + "different results: " + currentInstance + " & " + newInstance + ". This is likely " 71 + "due to a circular dependency."); 72 } 73 return newInstance; 74 } 75 76 /** Returns a {@link Provider} that caches the value from the given delegate provider. */ 77 // This method is declared this way instead of "<T> Provider<T> provider(Provider<T> delegate)" 78 // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949. provider(P delegate)79 public static <P extends Provider<T>, T> Provider<T> provider(P delegate) { 80 checkNotNull(delegate); 81 if (delegate instanceof DoubleCheck) { 82 /* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped 83 * binding, we shouldn't cache the value again. */ 84 return delegate; 85 } 86 return new DoubleCheck<T>(delegate); 87 } 88 89 /** Returns a {@link Lazy} that caches the value from the given provider. */ 90 // This method is declared this way instead of "<T> Lazy<T> lazy(Provider<T> delegate)" 91 // to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949. lazy(P provider)92 public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) { 93 if (provider instanceof Lazy) { 94 @SuppressWarnings("unchecked") 95 final Lazy<T> lazy = (Lazy<T>) provider; 96 // Avoids memoizing a value that is already memoized. 97 // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L 98 // are different types using covariant return on get(). Right now this is used with 99 // DoubleCheck<T> exclusively, which is implemented such that P and L are always 100 // the same, so it will be fine for that case. 101 return lazy; 102 } 103 return new DoubleCheck<T>(checkNotNull(provider)); 104 } 105 } 106