Skip to content

Commit 7987541

Browse files
committed
Streamlined caching for collection registrations.
1 parent b729975 commit 7987541

1 file changed

Lines changed: 70 additions & 66 deletions

File tree

src/Autofac/Features/Collections/CollectionRegistrationSource.cs

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

44
using System.Collections;
5+
using System.Collections.Concurrent;
56
using System.Linq.Expressions;
67
using System.Reflection;
78
using System.Runtime.CompilerServices;
@@ -191,35 +192,21 @@ private static Func<int, IList> GenerateArrayFactory(Type elementType)
191192
var cache = GetKeyedRegistrationCache(registry);
192193
var registryStamp = GetRegistryStamp(registry);
193194

194-
lock (cache)
195-
{
196-
if (cache.TryGetValue(elementType, out var entry) && entry.Stamp == registryStamp)
197-
{
198-
return entry.Registrations;
199-
}
200-
201-
var registrations = BuildKeyedRegistrationList(registry, elementType);
202-
cache[elementType] = new KeyedRegistrationCacheEntry(registryStamp, registrations);
203-
return registrations;
204-
}
195+
return cache.GetOrUpdate(
196+
elementType,
197+
registryStamp,
198+
() => BuildKeyedRegistrationList(registry, elementType));
205199
}
206200

207201
private static IReadOnlyList<ServiceRegistration> GetStandardRegistrationList(IComponentRegistry registry, Service elementTypeService)
208202
{
209203
var cache = GetStandardRegistrationCache(registry);
210204
var registryStamp = GetRegistryStamp(registry);
211205

212-
lock (cache)
213-
{
214-
if (cache.TryGetValue(elementTypeService, out var entry) && entry.Stamp == registryStamp)
215-
{
216-
return entry.Registrations;
217-
}
218-
219-
var registrations = BuildStandardRegistrationList(registry, elementTypeService);
220-
cache[elementTypeService] = new StandardRegistrationCacheEntry(registryStamp, registrations);
221-
return registrations;
222-
}
206+
return cache.GetOrUpdate(
207+
elementTypeService,
208+
registryStamp,
209+
() => BuildStandardRegistrationList(registry, elementTypeService));
223210
}
224211

225212
private static IReadOnlyList<ServiceRegistration> BuildStandardRegistrationList(IComponentRegistry registry, Service elementTypeService)
@@ -358,14 +345,19 @@ private static IList BuildCollection(
358345
return output;
359346
}
360347

361-
private static Dictionary<Type, KeyedRegistrationCacheEntry> GetKeyedRegistrationCache(IComponentRegistry registry)
348+
private static KeyedRegistrationCacheStore.RegistryCache GetKeyedRegistrationCache(IComponentRegistry registry)
362349
=> KeyedCacheStore.GetOrCreateRegistryCache(registry);
363350

364-
private static Dictionary<Service, StandardRegistrationCacheEntry> GetStandardRegistrationCache(IComponentRegistry registry)
351+
private static StandardRegistrationCacheStore.RegistryCache GetStandardRegistrationCache(IComponentRegistry registry)
365352
=> StandardCacheStore.GetOrCreateRegistryCache(registry);
366353

367354
private static int GetRegistryStamp(IComponentRegistry registry)
368355
{
356+
if (registry is IRegistrationDiagnostics diagnostics)
357+
{
358+
return diagnostics.RegistrationsVersion;
359+
}
360+
369361
if (registry.Registrations is ICollection<IComponentRegistration> collection)
370362
{
371363
return collection.Count;
@@ -414,11 +406,11 @@ private sealed class KeyedRegistrationCacheStore : IReflectionCache
414406

415407
public ReflectionCacheUsage Usage { get; } = ReflectionCacheUsage.All;
416408

417-
public Dictionary<Type, KeyedRegistrationCacheEntry> GetOrCreateRegistryCache(IComponentRegistry registry)
409+
public RegistryCache GetOrCreateRegistryCache(IComponentRegistry registry)
418410
{
419411
var cache = _registries.GetValue(registry, static _ => new RegistryCache());
420412
RegisterCacheReference(cache);
421-
return cache.Entries;
413+
return cache;
422414
}
423415

424416
public void Clear()
@@ -506,37 +498,43 @@ private void CleanupDeadRegistryReferences()
506498
/// <summary>
507499
/// Holds the cached keyed registrations for a single registry instance.
508500
/// </summary>
509-
private sealed class RegistryCache
501+
public sealed class RegistryCache
510502
{
503+
private readonly ConcurrentDictionary<Type, KeyedRegistrationCacheEntry> _entries = new();
511504
private int _registrationState;
512505

513-
public Dictionary<Type, KeyedRegistrationCacheEntry> Entries { get; } = new();
514-
515-
public void ClearAll()
506+
public IReadOnlyList<(Service Service, ServiceRegistration Registration)> GetOrUpdate(
507+
Type elementType,
508+
int stamp,
509+
Func<IReadOnlyList<(Service Service, ServiceRegistration Registration)>> valueFactory)
516510
{
517-
lock (Entries)
511+
if (_entries.TryGetValue(elementType, out var existing) && existing.Stamp == stamp)
518512
{
519-
Entries.Clear();
513+
return existing.Registrations;
520514
}
515+
516+
var updated = _entries.AddOrUpdate(
517+
elementType,
518+
_ => new KeyedRegistrationCacheEntry(stamp, valueFactory()),
519+
(_, current) => current.Stamp == stamp ? current : new KeyedRegistrationCacheEntry(stamp, valueFactory()));
520+
521+
return updated.Registrations;
521522
}
522523

523-
public void Clear(ReflectionCacheClearPredicate predicate)
524+
public void ClearAll()
524525
{
525-
lock (Entries)
526-
{
527-
if (Entries.Count == 0)
528-
{
529-
return;
530-
}
526+
_entries.Clear();
527+
}
531528

532-
var assemblySet = new HashSet<Assembly>();
533-
var keysToRemove = Entries.Keys
534-
.Where(key => predicate(key, TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(key, assemblySet)))
535-
.ToArray();
529+
public void Clear(ReflectionCacheClearPredicate predicate)
530+
{
531+
var assemblySet = new HashSet<Assembly>();
536532

537-
foreach (var key in keysToRemove)
533+
foreach (var key in _entries.Keys)
534+
{
535+
if (predicate(key, TypeAssemblyReferenceProvider.GetAllReferencedAssemblies(key, assemblySet)))
538536
{
539-
Entries.Remove(key);
537+
_entries.TryRemove(key, out _);
540538
}
541539
}
542540
}
@@ -561,11 +559,11 @@ private sealed class StandardRegistrationCacheStore : IReflectionCache
561559

562560
public ReflectionCacheUsage Usage { get; } = ReflectionCacheUsage.All;
563561

564-
public Dictionary<Service, StandardRegistrationCacheEntry> GetOrCreateRegistryCache(IComponentRegistry registry)
562+
public RegistryCache GetOrCreateRegistryCache(IComponentRegistry registry)
565563
{
566564
var cache = _registries.GetValue(registry, static _ => new RegistryCache());
567565
RegisterCacheReference(cache);
568-
return cache.Entries;
566+
return cache;
569567
}
570568

571569
public void Clear()
@@ -649,37 +647,43 @@ private void CleanupDeadRegistryReferences()
649647
}
650648
}
651649

652-
private sealed class RegistryCache
650+
public sealed class RegistryCache
653651
{
652+
private readonly ConcurrentDictionary<Service, StandardRegistrationCacheEntry> _entries = new();
654653
private int _registrationState;
655654

656-
public Dictionary<Service, StandardRegistrationCacheEntry> Entries { get; } = new();
657-
658-
public void ClearAll()
655+
public IReadOnlyList<ServiceRegistration> GetOrUpdate(
656+
Service service,
657+
int stamp,
658+
Func<IReadOnlyList<ServiceRegistration>> valueFactory)
659659
{
660-
lock (Entries)
660+
if (_entries.TryGetValue(service, out var existing) && existing.Stamp == stamp)
661661
{
662-
Entries.Clear();
662+
return existing.Registrations;
663663
}
664+
665+
var updated = _entries.AddOrUpdate(
666+
service,
667+
_ => new StandardRegistrationCacheEntry(stamp, valueFactory()),
668+
(_, current) => current.Stamp == stamp ? current : new StandardRegistrationCacheEntry(stamp, valueFactory()));
669+
670+
return updated.Registrations;
664671
}
665672

666-
public void Clear(ReflectionCacheClearPredicate predicate)
673+
public void ClearAll()
667674
{
668-
lock (Entries)
669-
{
670-
if (Entries.Count == 0)
671-
{
672-
return;
673-
}
675+
_entries.Clear();
676+
}
674677

675-
var assemblySet = new HashSet<Assembly>();
676-
var keysToRemove = Entries.Keys
677-
.Where(service => ShouldRemove(service, predicate, assemblySet))
678-
.ToArray();
678+
public void Clear(ReflectionCacheClearPredicate predicate)
679+
{
680+
var assemblySet = new HashSet<Assembly>();
679681

680-
foreach (var key in keysToRemove)
682+
foreach (var key in _entries.Keys)
683+
{
684+
if (ShouldRemove(key, predicate, assemblySet))
681685
{
682-
Entries.Remove(key);
686+
_entries.TryRemove(key, out _);
683687
}
684688
}
685689
}

0 commit comments

Comments
 (0)