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