protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument) { if (!_stackGuard.TryEnterOnCurrentStack()) { return(_stackGuard.RunOnEmptyStack((c, a) => VisitCallSite(c, a), callSite, argument)); } switch (callSite.Cache.Location) { case CallSiteResultCacheLocation.Root: return(VisitRootCache(callSite, argument)); case CallSiteResultCacheLocation.Scope: return(VisitScopeCache(callSite, argument)); case CallSiteResultCacheLocation.Dispose: return(VisitDisposeCache(callSite, argument)); case CallSiteResultCacheLocation.None: return(VisitNoCache(callSite, argument)); default: throw new ArgumentOutOfRangeException(); } }
private ServiceCallSite?CreateCallSite(Type serviceType, CallSiteChain callSiteChain) { if (!_stackGuard.TryEnterOnCurrentStack()) { return(_stackGuard.RunOnEmptyStack((type, chain) => CreateCallSite(type, chain), serviceType, callSiteChain)); } // We need to lock the resolution process for a single service type at a time: // Consider the following: // C -> D -> A // E -> D -> A // Resolving C and E in parallel means that they will be modifying the callsite cache concurrently // to add the entry for C and E, but the resolution of D and A is synchronized // to make sure C and E both reference the same instance of the callsite. // This is to make sure we can safely store singleton values on the callsites themselves var callsiteLock = _callSiteLocks.GetOrAdd(serviceType, static _ => new object()); lock (callsiteLock) { callSiteChain.CheckCircularDependency(serviceType); ServiceCallSite?callSite = TryCreateExact(serviceType, callSiteChain) ?? TryCreateOpenGeneric(serviceType, callSiteChain) ?? TryCreateEnumerable(serviceType, callSiteChain); return(callSite); } }