Skip to content

Commit

Permalink
Merge pull request #1423 from snaumenko-st/tracker-memory-leak
Browse files Browse the repository at this point in the history
FIX Memory leak in `DefaultRegisteredServicesTracker`
  • Loading branch information
alistairjevans authored Aug 30, 2024
2 parents 488294e + bb9e961 commit c12d527
Showing 1 changed file with 6 additions and 25 deletions.
31 changes: 6 additions & 25 deletions src/Autofac/Core/Registration/DefaultRegisteredServicesTracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ internal class DefaultRegisteredServicesTracker : Disposable, IRegisteredService
/// <summary>
/// External registration sources.
/// </summary>
private readonly List<IRegistrationSource> _dynamicRegistrationSources = new();
private readonly Stack<IRegistrationSource> _dynamicRegistrationSources = new();

/// <summary>
/// All registrations.
/// </summary>
private readonly ConcurrentBag<IComponentRegistration> _registrations = new();
private readonly ConcurrentQueue<IComponentRegistration> _registrations = new();

private readonly List<IServiceMiddlewareSource> _servicePipelineSources = new();

Expand Down Expand Up @@ -71,7 +71,7 @@ public IEnumerable<IComponentRegistration> Registrations
{
get
{
return _registrations.ToList();
return _registrations;
}
}

Expand All @@ -80,7 +80,7 @@ public IEnumerable<IRegistrationSource> Sources
{
get
{
return _dynamicRegistrationSources.ToList();
return _dynamicRegistrationSources;
}
}

Expand Down Expand Up @@ -117,7 +117,7 @@ public virtual void AddRegistration(IComponentRegistration registration, bool pr
// build pipelines for them.
// The Registrations collection is only available to consumers once the tracker is contained with a ContainerRegistry
// and the Complete method has been called.
_registrations.Add(registration);
_registrations.Enqueue(registration);
var handler = Registered;
handler?.Invoke(this, registration);

Expand All @@ -136,7 +136,7 @@ public void AddRegistrationSource(IRegistrationSource source)
throw new ArgumentNullException(nameof(source));
}

_dynamicRegistrationSources.Insert(0, source);
_dynamicRegistrationSources.Push(source);

var handler = RegistrationSourceAdded;
handler?.Invoke(this, source);
Expand Down Expand Up @@ -249,8 +249,6 @@ protected override void Dispose(bool disposing)
registration.Dispose();
}

ClearRegistrations();

base.Dispose(disposing);
}

Expand All @@ -262,26 +260,9 @@ protected override async ValueTask DisposeAsync(bool disposing)
await registration.DisposeAsync().ConfigureAwait(false);
}

ClearRegistrations();

// Do not call the base, otherwise the standard Dispose will fire.
}

private void ClearRegistrations()
{
// If we do not explicitly empty the ConcurrentBag that stores our registrations,
// this will cause a memory leak due to threads holding a reference to the bag.
// In netstandard2.0 the faster 'Clear' method is not available,
// so we have do this manually. We'll use the faster method if it's available though.
#if NETSTANDARD2_0
while (_registrations.TryTake(out _))
{
}
#else
_registrations.Clear();
#endif
}

private ServiceRegistrationInfo GetInitializedServiceInfo(Service service)
{
var createdEphemeralSet = false;
Expand Down

0 comments on commit c12d527

Please sign in to comment.