• 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 
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