diff --git a/src/Autofac/Core/Registration/ComponentRegistry.cs b/src/Autofac/Core/Registration/ComponentRegistry.cs index fe594aeaa..4c4c4c1fa 100644 --- a/src/Autofac/Core/Registration/ComponentRegistry.cs +++ b/src/Autofac/Core/Registration/ComponentRegistry.cs @@ -65,7 +65,7 @@ public class ComponentRegistry : Disposable, IComponentRegistry /// /// Keeps track of the status of registered services. /// - private readonly Dictionary _serviceInfo = new Dictionary(); + private readonly ConcurrentDictionary _serviceInfo = new ConcurrentDictionary(); private readonly ConcurrentDictionary> _decorators = new ConcurrentDictionary>(); @@ -118,9 +118,16 @@ public bool TryGetRegistration(Service service, out IComponentRegistration regis { if (service == null) throw new ArgumentNullException(nameof(service)); + // get without lock if already registered + var info = GetInitializedServiceInfoOrDefault(service); + if (info != null && info.TryGetRegistration(out registration)) + { + return true; + } + lock (_synchRoot) { - var info = GetInitializedServiceInfo(service); + info = GetInitializedServiceInfo(service); return info.TryGetRegistration(out registration); } } @@ -134,6 +141,13 @@ public bool IsRegistered(Service service) { if (service == null) throw new ArgumentNullException(nameof(service)); + // get without lock if already registered + var info = GetInitializedServiceInfoOrDefault(service); + if (info != null && info.IsRegistered) + { + return true; + } + lock (_synchRoot) { return GetInitializedServiceInfo(service).IsRegistered; @@ -233,9 +247,16 @@ public IEnumerable RegistrationsFor(Service service) { if (service == null) throw new ArgumentNullException(nameof(service)); + // get without lock if already registered + var info = GetInitializedServiceInfoOrDefault(service); + if (info != null) + { + return info.Implementations.ToArray(); + } + lock (_synchRoot) { - var info = GetInitializedServiceInfo(service); + info = GetInitializedServiceInfo(service); return info.Implementations.ToArray(); } } @@ -373,10 +394,21 @@ private ServiceRegistrationInfo GetServiceInfo(Service service) return existing; var info = new ServiceRegistrationInfo(service); - _serviceInfo.Add(service, info); + _serviceInfo.TryAdd(service, info); return info; } + /// + /// Returns ServiceInfo only in case it already exists and initialized for nolock resolution. + /// + private ServiceRegistrationInfo GetInitializedServiceInfoOrDefault(Service service) + { + ServiceRegistrationInfo existing; + if (_serviceInfo.TryGetValue(service, out existing) && existing.IsInitialized) + return existing; + return null; + } + private EventHandler GetRegistered() { if (Properties.TryGetValue(MetadataKeys.RegisteredPropertyKey, out var registered)) diff --git a/src/Autofac/Core/Registration/ServiceRegistrationInfo.cs b/src/Autofac/Core/Registration/ServiceRegistrationInfo.cs index 0a31ceec7..7d390d8e2 100644 --- a/src/Autofac/Core/Registration/ServiceRegistrationInfo.cs +++ b/src/Autofac/Core/Registration/ServiceRegistrationInfo.cs @@ -27,6 +27,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Threading; namespace Autofac.Core.Registration { @@ -41,21 +42,27 @@ internal class ServiceRegistrationInfo /// /// List of implicit default service implementations. Overriding default implementations are appended to the end, /// so the enumeration should begin from the end too, and the most default implementation comes last. + /// Used from multithreaded environment, use Interlocked/Volatile for read and write new List each time. /// - private readonly List _defaultImplementations = new List(); + private List _defaultImplementations = new List(); /// /// List of service implementations coming from sources. Sources have priority over preserve-default implementations. /// Implementations from sources are enumerated in preserve-default order, so the most default implementation comes first. + /// Used from multithreaded environment, use Interlocked/Volatile for read and write new List each time. /// private List _sourceImplementations = null; /// /// List of explicit service implementations specified with the PreserveExistingDefaults option. /// Enumerated in preserve-defaults order, so the most default implementation comes first. + /// Used from multithreaded environment, use Interlocked/Volatile for read and write new List each time. /// private List _preserveDefaultImplementations = null; + /// + /// Used from multithreaded environment, use Interlocked/Volatile for read and write each time. + /// [SuppressMessage("Microsoft.ApiDesignGuidelines", "CA2213", Justification = "The creator of the compponent registration is responsible for disposal.")] private IComponentRegistration _defaultImplementation; @@ -73,12 +80,25 @@ public ServiceRegistrationInfo(Service service) _service = service; } + private volatile bool _isInitialized; + /// /// Gets a value indicating whether the first time a service is requested, initialization (e.g. reading from sources) /// happens. This value will then be set to true. Calling many methods on this type before /// initialisation is an error. /// - public bool IsInitialized { get; private set; } + public bool IsInitialized + { + get + { + return _isInitialized; + } + + private set + { + _isInitialized = value; + } + } /// /// Gets the known implementations. The first implementation is a default one. @@ -88,15 +108,17 @@ public IEnumerable Implementations get { RequiresInitialization(); - var resultingCollection = Enumerable.Reverse(_defaultImplementations); - if (_sourceImplementations != null) + var resultingCollection = Enumerable.Reverse(Volatile.Read(ref _defaultImplementations)); + var sourceImplementations = Volatile.Read(ref _sourceImplementations); + if (sourceImplementations != null) { - resultingCollection = resultingCollection.Concat(_sourceImplementations); + resultingCollection = resultingCollection.Concat(sourceImplementations); } - if (_preserveDefaultImplementations != null) + var preserveDefaultImplementations = Volatile.Read(ref _preserveDefaultImplementations); + if (preserveDefaultImplementations != null) { - resultingCollection = resultingCollection.Concat(_preserveDefaultImplementations); + resultingCollection = resultingCollection.Concat(preserveDefaultImplementations); } return resultingCollection; @@ -122,9 +144,9 @@ public bool IsRegistered } private bool Any => - _defaultImplementations.Count > 0 || - _sourceImplementations != null || - _preserveDefaultImplementations != null; + Volatile.Read(ref _defaultImplementations).Count > 0 || + Volatile.Read(ref _sourceImplementations) != null || + Volatile.Read(ref _preserveDefaultImplementations) != null; public void AddImplementation(IComponentRegistration registration, bool preserveDefaults, bool originatedFromSource) { @@ -132,21 +154,29 @@ public void AddImplementation(IComponentRegistration registration, bool preserve { if (originatedFromSource) { - if (_sourceImplementations == null) + // called inside of lock, just making List _sourceImplementations thread safe + if (Volatile.Read(ref _sourceImplementations) == null) { - _sourceImplementations = new List(); + Volatile.Write(ref _sourceImplementations, new List()); } - _sourceImplementations.Add(registration); + var newSourceImplementations = new List(Volatile.Read(ref _sourceImplementations)); + newSourceImplementations.Add(registration); + + Volatile.Write(ref _sourceImplementations, newSourceImplementations); } else { - if (_preserveDefaultImplementations == null) + // called inside of lock, just making List _preserveDefaultImplementations thread safe + if (Volatile.Read(ref _preserveDefaultImplementations) == null) { - _preserveDefaultImplementations = new List(); + Volatile.Write(ref _preserveDefaultImplementations, new List()); } - _preserveDefaultImplementations.Add(registration); + var newPreserveDefaultImplementations = new List(Volatile.Read(ref _preserveDefaultImplementations)); + newPreserveDefaultImplementations.Add(registration); + + Volatile.Write(ref _preserveDefaultImplementations, newPreserveDefaultImplementations); } } else @@ -154,22 +184,48 @@ public void AddImplementation(IComponentRegistration registration, bool preserve if (originatedFromSource) throw new ArgumentOutOfRangeException(nameof(originatedFromSource)); - _defaultImplementations.Add(registration); + var newDefaultImplementations = new List(Volatile.Read(ref _defaultImplementations)); + newDefaultImplementations.Add(registration); + + Volatile.Write(ref _defaultImplementations, newDefaultImplementations); } - _defaultImplementation = null; + Volatile.Write(ref _defaultImplementation, null); } public bool TryGetRegistration(out IComponentRegistration registration) { RequiresInitialization(); - registration = _defaultImplementation ?? (_defaultImplementation = - _defaultImplementations.LastOrDefault() ?? - _sourceImplementations?.First() ?? - _preserveDefaultImplementations?.First()); + registration = Volatile.Read(ref _defaultImplementation); + if (registration != null) + return true; + + var defaultImplementations = Volatile.Read(ref _defaultImplementations); + if (defaultImplementations.Count > 0) + { + registration = defaultImplementations[defaultImplementations.Count - 1]; + Volatile.Write(ref _defaultImplementation, registration); + return true; + } + + var sourceImplementations = Volatile.Read(ref _sourceImplementations); + if (sourceImplementations != null && sourceImplementations.Count > 0) + { + registration = sourceImplementations[0]; + Volatile.Write(ref _defaultImplementation, registration); + return true; + } + + var preserveDefaultImplementations = Volatile.Read(ref _preserveDefaultImplementations); + if (preserveDefaultImplementations != null && preserveDefaultImplementations.Count > 0) + { + registration = preserveDefaultImplementations[0]; + Volatile.Write(ref _defaultImplementation, registration); + return true; + } - return registration != null; + return false; } public void Include(IRegistrationSource source) @@ -236,7 +292,7 @@ public bool ShouldRecalculateAdaptersOn(IComponentRegistration registration) // - Have already been initialized // - Were created via a registration source (because we might be adding an equivalent explicit registration such as Func) // - Don't contain any registrations (because a registration source was added when no adaptee was present) - return IsInitialized && (_sourceImplementations != null || !Any); + return IsInitialized && (Volatile.Read(ref _sourceImplementations) != null || !Any); } } } \ No newline at end of file