blob: e98a0303ecff312d3a89a145e92f29f8d9818695 [file] [log] [blame]
package com.google.inject.internal;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.OutOfScopeException;
import com.google.inject.Provider;
import com.google.inject.ProvisionException;
import com.google.inject.Scope;
import com.google.inject.Scopes;
import com.google.inject.Singleton;
/**
* One instance per {@link Injector}. Also see {@code @}{@link Singleton}.
*/
public class SingletonScope implements Scope {
/** A sentinel value representing null. */
private static final Object NULL = new Object();
/**
* Lock to use for new instances creation. This allows a per-root-Injector singleton lock,
* instead of a global lock across the JVM. Is set only during call to {@link #scope}.
*
* This is necessary because users have coded to a single {@link Scopes#SINGLETON} instance,
* and we cannot change that. Additionally, we can't reference the injector from a Key or
* Provider (the only variables available to the {@link #scope} method). Therefore, we rely
* on the injector implementation to explicitly set/unset the lock surrounding
* creation of the Provider the scope creates.
*
* @see {@link Scoping#scope(Key, InjectorImpl, InternalFactory, Object, Scoping)} for details.
*/
static final ThreadLocal<Object> singletonCreationPerRootInjectorLock =
new ThreadLocal<Object>();
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
// lock is referenced from anonymous class instance
final Object rootInjectorLock = singletonCreationPerRootInjectorLock.get();
if (rootInjectorLock == null) {
throw new OutOfScopeException("Singleton scope should only be used from Injector");
}
return new Provider<T>() {
/*
* The lazily initialized singleton instance. Once set, this will either have type T or will
* be equal to NULL.
*/
private volatile Object instance;
// DCL on a volatile is safe as of Java 5, which we obviously require.
@SuppressWarnings("DoubleCheckedLocking")
public T get() {
if (instance == null) {
/*
* Use a pretty coarse lock. We don't want to run into deadlocks
* when two threads try to load circularly-dependent objects.
* Maybe one of these days we will identify independent graphs of
* objects and offer to load them in parallel.
*
* This block is re-entrant for circular dependencies.
*/
synchronized (rootInjectorLock) {
if (instance == null) {
T provided = creator.get();
// don't remember proxies; these exist only to serve circular dependencies
if (Scopes.isCircularProxy(provided)) {
return provided;
}
Object providedOrSentinel = (provided == null) ? NULL : provided;
if (instance != null && instance != providedOrSentinel) {
throw new ProvisionException(
"Provider was reentrant while creating a singleton");
}
instance = providedOrSentinel;
}
}
}
Object localInstance = instance;
// This is safe because instance has type T or is equal to NULL
@SuppressWarnings("unchecked")
T returnedInstance = (localInstance != NULL) ? (T) localInstance : null;
return returnedInstance;
}
@Override
public String toString() {
return String.format("%s[%s]", creator, Scopes.SINGLETON);
}
};
}
@Override public String toString() {
return "Scopes.SINGLETON";
}
}