• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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