1package com.google.inject.internal;
2
3import com.google.common.base.Preconditions;
4import com.google.common.collect.ImmutableList;
5import com.google.common.collect.ImmutableMap;
6import com.google.common.collect.ListMultimap;
7import com.google.common.collect.Lists;
8import com.google.common.collect.Maps;
9import com.google.inject.Injector;
10import com.google.inject.Key;
11import com.google.inject.Provider;
12import com.google.inject.ProvisionException;
13import com.google.inject.Scope;
14import com.google.inject.Scopes;
15import com.google.inject.Singleton;
16import com.google.inject.internal.CycleDetectingLock.CycleDetectingLockFactory;
17import com.google.inject.spi.Dependency;
18import com.google.inject.spi.DependencyAndSource;
19import com.google.inject.spi.Message;
20
21import java.util.Collections;
22import java.util.List;
23import 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 */
72public 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