-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Making Singleton's creation lock less coarse.
Singleton is defined as a scope which creates no more than one object per injector. It's highly confusing when you catch a deadlock between two unrelated injectors due to the same class injection. Problem is demonstrated using a test that recreates scenario when one thread injecting class can block other thread to use its own injector. Proposed solution is to use Injector-wide locks in a singleton. ThreadLocal as a way to store current state. ------------- Created by MOE: http://code.google.com/p/moe-java MOE_MIGRATED_REVID=78469951
- Loading branch information
Showing
7 changed files
with
283 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
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"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.