1 package com.google.inject.internal; 2 3 import com.google.common.base.Preconditions; 4 import com.google.common.collect.ImmutableList; 5 import com.google.common.collect.ImmutableMap; 6 import com.google.common.collect.ListMultimap; 7 import com.google.common.collect.Lists; 8 import com.google.common.collect.Maps; 9 import com.google.inject.Injector; 10 import com.google.inject.Key; 11 import com.google.inject.Provider; 12 import com.google.inject.ProvisionException; 13 import com.google.inject.Scope; 14 import com.google.inject.Scopes; 15 import com.google.inject.Singleton; 16 import com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory; 17 import com.google.inject.spi.Dependency; 18 import com.google.inject.spi.DependencyAndSource; 19 import com.google.inject.spi.Message; 20 21 import java.util.Collections; 22 import java.util.List; 23 import java.util.Map; 24 25 /** 26 * One instance per {@link Injector}. Also see {@code @}{@link Singleton}. 27 * 28 * Introduction from the author: 29 * Implementation of this class seems unreasonably complicated at the first sight. 30 * I fully agree with you, that the beast below is very complex 31 * and it's hard to reason on how does it work or not. 32 * Still I want to assure you that hundreds(?) of hours were thrown 33 * into making this code simple, while still maintaining Singleton contract. 34 * 35 * Anyway, why is it so complex? Singleton scope does not seem to be that unique. 36 * 1) Guice has never truly expected to be used in multi threading environment 37 * with many Injectors working alongside each other. There is almost no 38 * code with Guice that propagates state between threads. And Singleton 39 * scope is The exception. 40 * 2) Guice supports circular dependencies and thus manages proxy objects. 41 * There is no interface that allows user defined Scopes to create proxies, 42 * it is expected to be done by Guice. Singleton scope needs to be 43 * able to detect circular dependencies spanning several threads, 44 * therefore Singleton scope needs to be able to create these proxies. 45 * 3) To make things worse, Guice has a very tricky definition for a binding 46 * resolution when Injectors are in in a parent/child relationship. 47 * And Scope does not have access to this information by design, 48 * the only real action that Scope can do is to call or not to call a creator. 49 * 4) There is no readily available code in Guice that can detect a potential 50 * deadlock, and no code for handling dependency cycles spanning several threads. 51 * This is significantly harder as all the dependencies in a thread at runtime 52 * can be represented with a list, where in a multi threaded environment 53 * we have more complex dependency trees. 54 * 5) Guice has a pretty strong contract regarding Garbage Collection, 55 * which often prevents us from linking objects directly. 56 * So simple domain specific code can not be written and intermediary 57 * id objects need to be managed. 58 * 6) Guice is relatively fast and we should not make things worse. 59 * We're trying our best to optimize synchronization for speed and memory. 60 * Happy path should be almost as fast as in a single threaded solution 61 * and should not take much more memory. 62 * 7) Error message generation in Guice was not meant to be used like this and to work around 63 * its APIs we need a lot of code. Additional complexity comes from inherent data races 64 * as message is only generated when failure occurs on proxy object generation. 65 * Things get ugly pretty fast. 66 * 67 * @see #scope(Key, Provider) 68 * @see CycleDetectingLock 69 * 70 * @author timofeyb (Timothy Basanov) 71 */ 72 public class SingletonScope implements Scope { 73 74 /** A sentinel value representing null. */ 75 private static final Object NULL = new Object(); 76 77 /** 78 * Allows us to detect when circular proxies are necessary. It's only used during singleton 79 * instance initialization, after initialization direct access through volatile field is used. 80 * 81 * NB: Factory uses {@link Key}s as a user locks ids, different injectors can 82 * share them. Cycles are detected properly as cycle detection does not rely on user locks ids, 83 * but error message generated could be less than ideal. 84 * 85 * TODO(user): we may use one factory per injector tree for optimization reasons 86 */ 87 private static final CycleDetectingLockFactory<Key<?>> cycleDetectingLockFactory = 88 new CycleDetectingLockFactory<Key<?>>(); 89 90 /** 91 * Provides singleton scope with the following properties: 92 * - creates no more than one instance per Key as a creator is used no more than once, 93 * - result is cached and returned quickly on subsequent calls, 94 * - exception in a creator is not treated as instance creation and is not cached, 95 * - creates singletons in parallel whenever possible, 96 * - waits for dependent singletons to be created even across threads and when dependencies 97 * are shared as long as no circular dependencies are detected, 98 * - returns circular proxy only when circular dependencies are detected, 99 * - aside from that, blocking synchronization is only used for proxy creation and initialization, 100 * @see CycleDetectingLockFactory 101 */ 102 public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) { 103 /** 104 * Locking strategy: 105 * - volatile instance: double-checked locking for quick exit when scope is initialized, 106 * - constructionContext: manipulations with proxies list or instance initialization 107 * - creationLock: singleton instance creation, 108 * -- allows to guarantee only one instance per singleton, 109 * -- special type of a lock, that prevents potential deadlocks, 110 * -- guards constructionContext for all operations except proxy creation 111 */ 112 return new Provider<T>() { 113 /** 114 * The lazily initialized singleton instance. Once set, this will either have type T or will 115 * be equal to NULL. Would never be reset to null. 116 */ 117 volatile Object instance; 118 119 /** 120 * Circular proxies are used when potential deadlocks are detected. Guarded by itself. 121 * ConstructionContext is not thread-safe, so each call should be synchronized. 122 */ 123 final ConstructionContext<T> constructionContext = new ConstructionContext<T>(); 124 125 /** For each binding there is a separate lock that we hold during object creation. */ 126 final CycleDetectingLock<Key<?>> creationLock = cycleDetectingLockFactory.create(key); 127 128 @SuppressWarnings("DoubleCheckedLocking") 129 public T get() { 130 // cache volatile variable for the usual case of already initialized object 131 final Object initialInstance = instance; 132 if (initialInstance == null) { 133 // instance is not initialized yet 134 135 // acquire lock for current binding to initialize an instance 136 final ListMultimap<Long, Key<?>> locksCycle = 137 creationLock.lockOrDetectPotentialLocksCycle(); 138 if (locksCycle.isEmpty()) { 139 // this thread now owns creation of an instance 140 try { 141 // intentionally reread volatile variable to prevent double initialization 142 if (instance == null) { 143 // creator throwing an exception can cause circular proxies created in 144 // different thread to never be resolved, just a warning 145 T provided = creator.get(); 146 Object providedNotNull = provided == null ? NULL : provided; 147 148 // scope called recursively can initialize instance as a side effect 149 if (instance == null) { 150 // instance is still not initialized, se we can proceed 151 152 // don't remember proxies created by Guice on circular dependency 153 // detection within the same thread; they are not real instances to cache 154 if (Scopes.isCircularProxy(provided)) { 155 return provided; 156 } 157 158 synchronized (constructionContext) { 159 // guarantee thread-safety for instance and proxies initialization 160 instance = providedNotNull; 161 constructionContext.setProxyDelegates(provided); 162 } 163 } else { 164 // safety assert in case instance was initialized 165 Preconditions.checkState(instance == providedNotNull, 166 "Singleton is called recursively returning different results"); 167 } 168 } 169 } catch (RuntimeException e) { 170 // something went wrong, be sure to clean a construction context 171 // this helps to prevent potential memory leaks in circular proxies list 172 synchronized (constructionContext) { 173 constructionContext.finishConstruction(); 174 } 175 throw e; 176 } finally { 177 // always release our creation lock, even on failures 178 creationLock.unlock(); 179 } 180 } else { 181 // potential deadlock detected, creation lock is not taken by this thread 182 synchronized (constructionContext) { 183 // guarantee thread-safety for instance and proxies initialization 184 if (instance == null) { 185 // InjectorImpl.callInContext() sets this context when scope is called from Guice 186 Map<Thread, InternalContext> globalInternalContext = 187 InjectorImpl.getGlobalInternalContext(); 188 InternalContext internalContext = globalInternalContext.get(Thread.currentThread()); 189 190 // creating a proxy to satisfy circular dependency across several threads 191 Dependency<?> dependency = Preconditions.checkNotNull( 192 internalContext.getDependency(), 193 "globalInternalContext.get(currentThread()).getDependency()"); 194 Class<?> rawType = dependency.getKey().getTypeLiteral().getRawType(); 195 196 try { 197 @SuppressWarnings("unchecked") 198 T proxy = (T) constructionContext.createProxy( 199 new Errors(), internalContext.getInjectorOptions(), rawType); 200 return proxy; 201 } catch (ErrorsException e) { 202 // best effort to create a rich error message 203 List<Message> exceptionErrorMessages = e.getErrors().getMessages(); 204 // we expect an error thrown 205 Preconditions.checkState(exceptionErrorMessages.size() == 1); 206 // explicitly copy the map to guarantee iteration correctness 207 // it's ok to have a data race with other threads that are locked 208 Message cycleDependenciesMessage = createCycleDependenciesMessage( 209 ImmutableMap.copyOf(globalInternalContext), 210 locksCycle, 211 exceptionErrorMessages.get(0)); 212 // adding stack trace generated by us in addition to a standard one 213 throw new ProvisionException(ImmutableList.of( 214 cycleDependenciesMessage, exceptionErrorMessages.get(0))); 215 } 216 } 217 } 218 } 219 // at this point we're sure that singleton was initialized, 220 // reread volatile variable to catch all corner cases 221 222 // caching volatile variable to minimize number of reads performed 223 final Object initializedInstance = instance; 224 Preconditions.checkState(initializedInstance != null, 225 "Internal error: Singleton is not initialized contrary to our expectations"); 226 @SuppressWarnings("unchecked") 227 T initializedTypedInstance = (T) initializedInstance; 228 return initializedInstance == NULL ? null : initializedTypedInstance; 229 } else { 230 // singleton is already initialized and local cache can be used 231 @SuppressWarnings("unchecked") 232 T typedInitialIntance = (T) initialInstance; 233 return initialInstance == NULL ? null : typedInitialIntance; 234 } 235 } 236 237 /** 238 * Helper method to create beautiful and rich error descriptions. Best effort and slow. 239 * Tries its best to provide dependency information from injectors currently available 240 * in a global internal context. 241 * 242 * <p>The main thing being done is creating a list of Dependencies involved into 243 * lock cycle across all the threads involved. This is a structure we're creating: 244 * <pre> 245 * { Current Thread, C.class, B.class, Other Thread, B.class, C.class, Current Thread } 246 * To be inserted in the beginning by Guice: { A.class, B.class, C.class } 247 * </pre> 248 * When we're calling Guice to create A and it fails in the deadlock while trying to 249 * create C, which is being created by another thread, which waits for B. List would 250 * be reversed before printing it to the end user. 251 */ 252 private Message createCycleDependenciesMessage( 253 Map<Thread, InternalContext> globalInternalContext, 254 ListMultimap<Long, Key<?>> locksCycle, 255 Message proxyCreationError) { 256 // this is the main thing that we'll show in an error message, 257 // current thread is populate by Guice 258 List<Object> sourcesCycle = Lists.newArrayList(); 259 sourcesCycle.add(Thread.currentThread()); 260 // temp map to speed up look ups 261 Map<Long, Thread> threadById = Maps.newHashMap(); 262 for (Thread thread : globalInternalContext.keySet()) { 263 threadById.put(thread.getId(), thread); 264 } 265 for (long lockedThreadId : locksCycle.keySet()) { 266 Thread lockedThread = threadById.get(lockedThreadId); 267 List<Key<?>> lockedKeys = Collections.unmodifiableList(locksCycle.get(lockedThreadId)); 268 if (lockedThread == null) { 269 // thread in a lock cycle is already terminated 270 continue; 271 } 272 List<DependencyAndSource> dependencyChain = null; 273 boolean allLockedKeysAreFoundInDependencies = false; 274 // thread in a cycle is still present 275 InternalContext lockedThreadInternalContext = globalInternalContext.get(lockedThread); 276 if (lockedThreadInternalContext != null) { 277 dependencyChain = lockedThreadInternalContext.getDependencyChain(); 278 279 // check that all of the keys are still present in dependency chain in order 280 List<Key<?>> lockedKeysToFind = Lists.newLinkedList(lockedKeys); 281 // check stack trace of the thread 282 for (DependencyAndSource d : dependencyChain) { 283 Dependency<?> dependency = d.getDependency(); 284 if (dependency == null) { 285 continue; 286 } 287 if (dependency.getKey().equals(lockedKeysToFind.get(0))) { 288 lockedKeysToFind.remove(0); 289 if (lockedKeysToFind.isEmpty()) { 290 // everything is found! 291 allLockedKeysAreFoundInDependencies = true; 292 break; 293 } 294 } 295 } 296 } 297 if (allLockedKeysAreFoundInDependencies) { 298 // all keys are present in a dependency chain of a thread's last injector, 299 // highly likely that we just have discovered a dependency 300 // chain that is part of a lock cycle starting with the first lock owned 301 Key<?> firstLockedKey = lockedKeys.get(0); 302 boolean firstLockedKeyFound = false; 303 for (DependencyAndSource d : dependencyChain) { 304 Dependency<?> dependency = d.getDependency(); 305 if (dependency == null) { 306 continue; 307 } 308 if (firstLockedKeyFound) { 309 sourcesCycle.add(dependency); 310 sourcesCycle.add(d.getBindingSource()); 311 } else if (dependency.getKey().equals(firstLockedKey)) { 312 firstLockedKeyFound = true; 313 // for the very first one found we don't care why, so no dependency is added 314 sourcesCycle.add(d.getBindingSource()); 315 } 316 } 317 } else { 318 // something went wrong and not all keys are present in a state of an injector 319 // that was used last for a current thread. 320 // let's add all keys we're aware of, still better than nothing 321 sourcesCycle.addAll(lockedKeys); 322 } 323 // mentions that a tread is a part of a cycle 324 sourcesCycle.add(lockedThread); 325 } 326 return new Message( 327 sourcesCycle, 328 String.format("Encountered circular dependency spanning several threads. %s", 329 proxyCreationError.getMessage()), 330 null); 331 } 332 333 @Override 334 public String toString() { 335 return String.format("%s[%s]", creator, Scopes.SINGLETON); 336 } 337 }; 338 } 339 340 @Override public String toString() { 341 return "Scopes.SINGLETON"; 342 } 343 } 344