protected override object VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context) { // Check if we are in the situation where scoped service was promoted to singleton // and we need to lock the root RuntimeResolverLock requiredScope = context.Scope == context.Scope.Engine.Root ? RuntimeResolverLock.Root : RuntimeResolverLock.Scope; return(VisitCache(singletonCallSite, context, context.Scope, requiredScope)); }
private object VisitCache(ServiceCallSite scopedCallSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) { bool lockTaken = false; var resolvedServices = serviceProviderEngine.ResolvedServices; // Taking locks only once allows us to fork resolution process // on another thread without causing the deadlock because we // always know that we are going to wait the other thread to finish before // releasing the lock if ((context.AcquiredLocks & lockType) == 0) { Monitor.Enter(resolvedServices, ref lockTaken); } try { if (!resolvedServices.TryGetValue(scopedCallSite.Cache.Key, out var resolved)) { resolved = VisitCallSiteMain(scopedCallSite, new RuntimeResolverContext { Scope = serviceProviderEngine, AcquiredLocks = context.AcquiredLocks | lockType }); serviceProviderEngine.CaptureDisposable(resolved); resolvedServices.Add(scopedCallSite.Cache.Key, resolved); } return(resolved); } finally { if (lockTaken) { Monitor.Exit(resolvedServices); } } }
private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) { bool lockTaken = false; Dictionary <ServiceCacheKey, object> resolvedServices = serviceProviderEngine.ResolvedServices; // Taking locks only once allows us to fork resolution process // on another thread without causing the deadlock because we // always know that we are going to wait the other thread to finish before // releasing the lock if ((context.AcquiredLocks & lockType) == 0) { Monitor.Enter(resolvedServices, ref lockTaken); } try { return(ResolveService(callSite, context, lockType, serviceProviderEngine)); } finally { if (lockTaken) { Monitor.Exit(resolvedServices); } } }
private object ResolveService(ServiceCallSite callSite, RuntimeResolverContext context, RuntimeResolverLock lockType, ServiceProviderEngineScope serviceProviderEngine) { Dictionary <ServiceCacheKey, object> resolvedServices = serviceProviderEngine.ResolvedServices; if (!resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved)) { resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext { Scope = serviceProviderEngine, AcquiredLocks = context.AcquiredLocks | lockType }); serviceProviderEngine.CaptureDisposable(resolved); resolvedServices.Add(callSite.Cache.Key, resolved); } return(resolved); }
private object VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType) { bool lockTaken = false; object sync = serviceProviderEngine.Sync; Dictionary <ServiceCacheKey, object> resolvedServices = serviceProviderEngine.ResolvedServices; // Taking locks only once allows us to fork resolution process // on another thread without causing the deadlock because we // always know that we are going to wait the other thread to finish before // releasing the lock if ((context.AcquiredLocks & lockType) == 0) { Monitor.Enter(sync, ref lockTaken); } try { // Note: This method has already taken lock by the caller for resolution and access synchronization. // For scoped: takes a dictionary as both a resolution lock and a dictionary access lock. if (resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved)) { return(resolved); } resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext { Scope = serviceProviderEngine, AcquiredLocks = context.AcquiredLocks | lockType }); serviceProviderEngine.CaptureDisposable(resolved); resolvedServices.Add(callSite.Cache.Key, resolved); return(resolved); } finally { if (lockTaken) { Monitor.Exit(sync); } } }
private object ResolveService(ServiceCallSite callSite, RuntimeResolverContext context, RuntimeResolverLock lockType, ServiceProviderEngineScope serviceProviderEngine) { IDictionary <ServiceCacheKey, object> resolvedServices = serviceProviderEngine.ResolvedServices; // Note: This method has already taken lock by the caller for resolution and access synchronization. // For root: uses a concurrent dictionary and takes a per singleton lock for resolution. // For scoped: takes a dictionary as both a resolution lock and a dictionary access lock. Debug.Assert( (lockType == RuntimeResolverLock.Root && resolvedServices is ConcurrentDictionary <ServiceCacheKey, object>) || (lockType == RuntimeResolverLock.Scope && Monitor.IsEntered(resolvedServices))); if (resolvedServices.TryGetValue(callSite.Cache.Key, out object resolved)) { return(resolved); } resolved = VisitCallSiteMain(callSite, new RuntimeResolverContext { Scope = serviceProviderEngine, AcquiredLocks = context.AcquiredLocks | lockType }); serviceProviderEngine.CaptureDisposable(resolved); resolvedServices.Add(callSite.Cache.Key, resolved); return(resolved); }