Skip to content

Commit dbb6f7d

Browse files
committed
Simplify circular dependency detection caching.
1 parent f1c1ab7 commit dbb6f7d

3 files changed

Lines changed: 125 additions & 1 deletion

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
// Copyright (c) Autofac Project. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
using System.Collections.Generic;
5+
using System.Runtime.CompilerServices;
6+
using Autofac.Core.Resolving.Pipeline;
7+
8+
namespace Autofac.Core.Resolving;
9+
10+
/// <summary>
11+
/// Tracks registrations encountered in a resolve operation to accelerate circular dependency checks.
12+
/// </summary>
13+
internal sealed class DependencyDetectionState
14+
{
15+
private static readonly IEqualityComparer<IComponentRegistration> ReferenceComparer = new ComponentRegistrationReferenceComparer();
16+
private readonly Dictionary<IComponentRegistration, int> _registrations = new(ReferenceComparer);
17+
18+
/// <summary>
19+
/// Gets a value indicating whether the state has been initialized from the active request stack.
20+
/// </summary>
21+
public bool IsInitialized { get; private set; }
22+
23+
/// <summary>
24+
/// Initializes the state from the existing request stack, if not already initialized.
25+
/// </summary>
26+
/// <param name="existingStack">The current request stack segment.</param>
27+
public void Initialize(IEnumerable<ResolveRequestContext> existingStack)
28+
{
29+
if (IsInitialized)
30+
{
31+
return;
32+
}
33+
34+
foreach (var context in existingStack)
35+
{
36+
AddRegistration(context.Registration);
37+
}
38+
39+
IsInitialized = true;
40+
}
41+
42+
/// <summary>
43+
/// Returns true if the registration is currently tracked.
44+
/// </summary>
45+
/// <param name="registration">The registration to check.</param>
46+
/// <returns>True if the registration is tracked.</returns>
47+
public bool Contains(IComponentRegistration registration)
48+
{
49+
return IsInitialized && _registrations.ContainsKey(registration);
50+
}
51+
52+
/// <summary>
53+
/// Adds a registration to the tracked set.
54+
/// </summary>
55+
/// <param name="registration">The registration to add.</param>
56+
public void Push(IComponentRegistration registration)
57+
{
58+
if (!IsInitialized)
59+
{
60+
return;
61+
}
62+
63+
AddRegistration(registration);
64+
}
65+
66+
/// <summary>
67+
/// Removes a registration from the tracked set.
68+
/// </summary>
69+
/// <param name="registration">The registration to remove.</param>
70+
public void Pop(IComponentRegistration registration)
71+
{
72+
if (!IsInitialized || !_registrations.TryGetValue(registration, out var count))
73+
{
74+
return;
75+
}
76+
77+
if (count <= 1)
78+
{
79+
_registrations.Remove(registration);
80+
}
81+
else
82+
{
83+
_registrations[registration] = count - 1;
84+
}
85+
}
86+
87+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
88+
private void AddRegistration(IComponentRegistration registration)
89+
{
90+
if (_registrations.TryGetValue(registration, out var count))
91+
{
92+
_registrations[registration] = count + 1;
93+
}
94+
else
95+
{
96+
_registrations.Add(registration, 1);
97+
}
98+
}
99+
100+
private sealed class ComponentRegistrationReferenceComparer : IEqualityComparer<IComponentRegistration>
101+
{
102+
public bool Equals(IComponentRegistration? x, IComponentRegistration? y) => ReferenceEquals(x, y);
103+
104+
public int GetHashCode(IComponentRegistration obj) => RuntimeHelpers.GetHashCode(obj);
105+
}
106+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright (c) Autofac Project. All rights reserved.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace Autofac.Core.Resolving;
5+
6+
/// <summary>
7+
/// Provides a place to store dependency detection state for a resolve operation.
8+
/// </summary>
9+
internal interface IDependencyDetectionStateOwner
10+
{
11+
/// <summary>
12+
/// Gets or sets the cached dependency detection state.
13+
/// </summary>
14+
DependencyDetectionState? DependencyDetectionState { get; set; }
15+
}

src/Autofac/Core/Resolving/ResolveOperation.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Autofac.Core.Resolving;
1212
/// A <see cref="ResolveOperation"/> is a component context that sequences and monitors the multiple
1313
/// activations that go into producing a single requested object graph.
1414
/// </summary>
15-
internal sealed class ResolveOperation : IDependencyTrackingResolveOperation
15+
internal sealed class ResolveOperation : IDependencyTrackingResolveOperation, IDependencyDetectionStateOwner
1616
{
1717
private const int SuccessListInitialCapacity = 32;
1818
private readonly List<DefaultResolveRequestContext> _successfulRequests = new(SuccessListInitialCapacity);
@@ -68,6 +68,9 @@ public ResolveOperation(
6868
/// <inheritdoc/>
6969
public SegmentedStack<ResolveRequestContext> RequestStack { get; } = new SegmentedStack<ResolveRequestContext>();
7070

71+
/// <inheritdoc/>
72+
public DependencyDetectionState? DependencyDetectionState { get; set; }
73+
7174
/// <summary>
7275
/// Gets the <see cref="ResolveRequest" /> that initiated the operation. Other nested requests may have been
7376
/// issued as a result of this one.

0 commit comments

Comments
 (0)