From d8e84363082681b59d9c9c2777af9dc5eac5c287 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 22 Aug 2022 13:25:48 -0700 Subject: [PATCH] [release/7.0-rc1] Fix leak caused by not disposing the scoped parent service provider (#74362) * Fix leaks caused by not disposing the parent scoped ServiceProvider * Address the feedback Co-authored-by: Tarek Mahmoud Sayed --- .../ServiceLookup/ServiceProviderEngineScope.cs | 16 ++++++++++++---- .../src/ServiceProvider.cs | 2 ++ .../DI.Tests/ServiceProviderEngineScopeTests.cs | 12 ++++++++++++ 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index 0cddf30f2a719..d1af024a3e38e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -187,12 +187,20 @@ static async ValueTask Await(int i, ValueTask vt, List toDispose) // No further changes to _state.Disposables, are allowed. _disposed = true; - // ResolvedServices is never cleared for singletons because there might be a compilation running in background - // trying to get a cached singleton service. If it doesn't find it - // it will try to create a new one which will result in an ObjectDisposedException. + } - return _disposables; + if (IsRootScope && !RootProvider.IsDisposed()) + { + // If this ServiceProviderEngineScope instance is a root scope, disposing this instance will need to dispose the RootProvider too. + // Otherwise the RootProvider will never get disposed and will leak. + // Note, if the RootProvider get disposed first, it will automatically dispose all attached ServiceProviderEngineScope objects. + RootProvider.Dispose(); } + + // ResolvedServices is never cleared for singletons because there might be a compilation running in background + // trying to get a cached singleton service. If it doesn't find it + // it will try to create a new one which will result in an ObjectDisposedException. + return _disposables; } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index f66f36b3cf6ed..dd9b1af11a55a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -89,6 +89,8 @@ internal ServiceProvider(ICollection serviceDescriptors, Serv /// The service that was produced. public object? GetService(Type serviceType) => GetService(serviceType, Root); + internal bool IsDisposed() => _disposed; + /// public void Dispose() { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs index d43752db21eba..e801497236f0b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; using Microsoft.Extensions.DependencyInjection.Specification.Fakes; using Xunit; @@ -17,5 +18,16 @@ public void DoubleDisposeWorks() serviceProviderEngineScope.Dispose(); serviceProviderEngineScope.Dispose(); } + + [Fact] + public void RootEngineScopeDisposeTest() + { + var services = new ServiceCollection(); + ServiceProvider sp = services.BuildServiceProvider(); + var s = sp.GetRequiredService(); + ((IDisposable)s).Dispose(); + + Assert.Throws(() => sp.GetRequiredService()); + } } }