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 import static dagger.internal.Providers.asDaggerProvider; 21 22 import dagger.Lazy; 23 import org.jspecify.annotations.Nullable; 24 25 /** 26 * A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a 27 * delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>. 28 */ 29 public final class DoubleCheck<T extends @Nullable Object> implements Provider<T>, Lazy<T> { 30 private static final Object UNINITIALIZED = new Object(); 31 32 private volatile @Nullable Provider<T> provider; 33 private volatile @Nullable Object instance = UNINITIALIZED; 34 DoubleCheck(Provider<T> provider)35 private DoubleCheck(Provider<T> provider) { 36 assert provider != null; 37 this.provider = provider; 38 } 39 40 @SuppressWarnings("unchecked") // cast only happens when result comes from the provider 41 @Override get()42 public T get() { 43 @Nullable Object result = instance; 44 if (result == UNINITIALIZED) { 45 result = getSynchronized(); 46 } 47 return (T) result; 48 } 49 50 @SuppressWarnings("nullness:dereference.of.nullable") // provider is non-null getSynchronized()51 private synchronized @Nullable Object getSynchronized() { 52 @Nullable Object result = instance; 53 if (result == UNINITIALIZED) { 54 result = provider.get(); 55 instance = reentrantCheck(instance, result); 56 /* Null out the reference to the provider. We are never going to need it again, so we 57 * can make it eligible for GC. */ 58 provider = null; 59 } 60 return result; 61 } 62 63 /** 64 * Checks to see if creating the new instance has resulted in a recursive call. If it has, and the 65 * new instance is the same as the current instance, return the instance. However, if the new 66 * instance differs from the current instance, an {@link IllegalStateException} is thrown. 67 */ reentrantCheck( @ullable Object currentInstance, @Nullable Object newInstance)68 private static @Nullable Object reentrantCheck( 69 @Nullable Object currentInstance, @Nullable Object newInstance) { 70 boolean isReentrant = currentInstance != UNINITIALIZED; 71 if (isReentrant && currentInstance != newInstance) { 72 throw new IllegalStateException("Scoped provider was invoked recursively returning " 73 + "different results: " + currentInstance + " & " + newInstance + ". This is likely " 74 + "due to a circular dependency."); 75 } 76 return newInstance; 77 } 78 79 /** Returns a {@link Provider} that caches the value from the given delegate provider. */ provider(dagger.internal.Provider<T> delegate)80 public static <T> dagger.internal.Provider<T> provider(dagger.internal.Provider<T> delegate) { 81 checkNotNull(delegate); 82 if (delegate instanceof DoubleCheck) { 83 /* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped 84 * binding, we shouldn't cache the value again. */ 85 return delegate; 86 } 87 return new DoubleCheck<T>(delegate); 88 } 89 90 /** 91 * Legacy javax version of the method to support libraries compiled with an older version of 92 * Dagger. Do not use directly. 93 */ 94 @Deprecated provider( P delegate)95 public static <P extends javax.inject.Provider<T>, T> javax.inject.Provider<T> provider( 96 P delegate) { 97 return provider(asDaggerProvider(delegate)); 98 } 99 100 /** Returns a {@link Lazy} that caches the value from the given provider. */ lazy(Provider<T> provider)101 public static <T> Lazy<T> lazy(Provider<T> provider) { 102 if (provider instanceof Lazy) { 103 @SuppressWarnings("unchecked") 104 final Lazy<T> lazy = (Lazy<T>) provider; 105 // Avoids memoizing a value that is already memoized. 106 // NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L 107 // are different types using covariant return on get(). Right now this is used with 108 // DoubleCheck<T> exclusively, which is implemented such that P and L are always 109 // the same, so it will be fine for that case. 110 return lazy; 111 } 112 return new DoubleCheck<T>(checkNotNull(provider)); 113 } 114 115 /** 116 * Legacy javax version of the method to support libraries compiled with an older version of 117 * Dagger. Do not use directly. 118 */ lazy(P provider)119 public static <P extends javax.inject.Provider<T>, T> Lazy<T> lazy(P provider) { 120 return lazy(asDaggerProvider(provider)); 121 } 122 } 123