106 lines
4.3 KiB
106 lines
4.3 KiB
/*
|
|
* Copyright (C) 2016 The Dagger Authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package dagger.internal;
|
|
|
|
import static dagger.internal.Preconditions.checkNotNull;
|
|
|
|
import dagger.Lazy;
|
|
import javax.inject.Provider;
|
|
|
|
/**
|
|
* A {@link Lazy} and {@link Provider} implementation that memoizes the value returned from a
|
|
* delegate using the double-check idiom described in Item 71 of <i>Effective Java 2</i>.
|
|
*/
|
|
public final class DoubleCheck<T> implements Provider<T>, Lazy<T> {
|
|
private static final Object UNINITIALIZED = new Object();
|
|
|
|
private volatile Provider<T> provider;
|
|
private volatile Object instance = UNINITIALIZED;
|
|
|
|
private DoubleCheck(Provider<T> provider) {
|
|
assert provider != null;
|
|
this.provider = provider;
|
|
}
|
|
|
|
@SuppressWarnings("unchecked") // cast only happens when result comes from the provider
|
|
@Override
|
|
public T get() {
|
|
Object result = instance;
|
|
if (result == UNINITIALIZED) {
|
|
synchronized (this) {
|
|
result = instance;
|
|
if (result == UNINITIALIZED) {
|
|
result = provider.get();
|
|
instance = reentrantCheck(instance, result);
|
|
/* Null out the reference to the provider. We are never going to need it again, so we
|
|
* can make it eligible for GC. */
|
|
provider = null;
|
|
}
|
|
}
|
|
}
|
|
return (T) result;
|
|
}
|
|
|
|
/**
|
|
* Checks to see if creating the new instance has resulted in a recursive call. If it has, and the
|
|
* new instance is the same as the current instance, return the instance. However, if the new
|
|
* instance differs from the current instance, an {@link IllegalStateException} is thrown.
|
|
*/
|
|
public static Object reentrantCheck(Object currentInstance, Object newInstance) {
|
|
boolean isReentrant = !(currentInstance == UNINITIALIZED
|
|
// This check is needed for fastInit's implementation, which uses MemoizedSentinel types.
|
|
|| currentInstance instanceof MemoizedSentinel);
|
|
|
|
if (isReentrant && currentInstance != newInstance) {
|
|
throw new IllegalStateException("Scoped provider was invoked recursively returning "
|
|
+ "different results: " + currentInstance + " & " + newInstance + ". This is likely "
|
|
+ "due to a circular dependency.");
|
|
}
|
|
return newInstance;
|
|
}
|
|
|
|
/** Returns a {@link Provider} that caches the value from the given delegate provider. */
|
|
// This method is declared this way instead of "<T> Provider<T> provider(Provider<T> delegate)"
|
|
// to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
|
|
public static <P extends Provider<T>, T> Provider<T> provider(P delegate) {
|
|
checkNotNull(delegate);
|
|
if (delegate instanceof DoubleCheck) {
|
|
/* This should be a rare case, but if we have a scoped @Binds that delegates to a scoped
|
|
* binding, we shouldn't cache the value again. */
|
|
return delegate;
|
|
}
|
|
return new DoubleCheck<T>(delegate);
|
|
}
|
|
|
|
/** Returns a {@link Lazy} that caches the value from the given provider. */
|
|
// This method is declared this way instead of "<T> Lazy<T> lazy(Provider<T> delegate)"
|
|
// to work around an Eclipse type inference bug: https://github.com/google/dagger/issues/949.
|
|
public static <P extends Provider<T>, T> Lazy<T> lazy(P provider) {
|
|
if (provider instanceof Lazy) {
|
|
@SuppressWarnings("unchecked")
|
|
final Lazy<T> lazy = (Lazy<T>) provider;
|
|
// Avoids memoizing a value that is already memoized.
|
|
// NOTE: There is a pathological case where Provider<P> may implement Lazy<L>, but P and L
|
|
// are different types using covariant return on get(). Right now this is used with
|
|
// DoubleCheck<T> exclusively, which is implemented such that P and L are always
|
|
// the same, so it will be fine for that case.
|
|
return lazy;
|
|
}
|
|
return new DoubleCheck<T>(checkNotNull(provider));
|
|
}
|
|
}
|