From 721cf1fd04f906d3d4c5c2d561c594709c5744a8 Mon Sep 17 00:00:00 2001 From: martincostello Date: Sun, 17 Oct 2021 15:11:34 +0100 Subject: [PATCH 1/2] Fix IHost being disposed of twice Fix the IHost wrapped by DeferredHost being disposed of twice if the IHost implementation implements IAsyncDisposable. --- src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs b/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs index ea58bf4faade..14194b0a1070 100644 --- a/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs +++ b/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs @@ -141,7 +141,10 @@ public async ValueTask DisposeAsync() await disposable.DisposeAsync().ConfigureAwait(false); return; } - Dispose(); + else + { + _host.Dispose(); + } } public async Task StartAsync(CancellationToken cancellationToken = default) From 73018234aadcf0a662375f29e6fb6613a511124d Mon Sep 17 00:00:00 2001 From: martincostello Date: Sun, 17 Oct 2021 17:15:15 +0100 Subject: [PATCH 2/2] Do not dispose the host twice Do not dispose the IHost multiple times if disposed more than once. --- src/DefaultBuilder/src/WebApplication.cs | 20 ++++++++++++++++-- .../Mvc.Testing/src/DeferredHostBuilder.cs | 21 +++++++++++-------- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/DefaultBuilder/src/WebApplication.cs b/src/DefaultBuilder/src/WebApplication.cs index 9d0a837c8300..e9dcd85efc20 100644 --- a/src/DefaultBuilder/src/WebApplication.cs +++ b/src/DefaultBuilder/src/WebApplication.cs @@ -24,6 +24,8 @@ public sealed class WebApplication : IHost, IApplicationBuilder, IEndpointRouteB private readonly IHost _host; private readonly List _dataSources = new(); + private bool _disposed; + internal WebApplication(IHost host) { _host = host; @@ -162,12 +164,26 @@ public void Run(string? url = null) /// /// Disposes the application. /// - void IDisposable.Dispose() => _host.Dispose(); + void IDisposable.Dispose() + { + if (!_disposed) + { + _host.Dispose(); + _disposed = true; + } + } /// /// Disposes the application. /// - public ValueTask DisposeAsync() => ((IAsyncDisposable)_host).DisposeAsync(); + public async ValueTask DisposeAsync() + { + if (!_disposed) + { + await ((IAsyncDisposable)_host).DisposeAsync(); + _disposed = true; + } + } internal RequestDelegate BuildRequestDelegate() => ApplicationBuilder.Build(); RequestDelegate IApplicationBuilder.Build() => BuildRequestDelegate(); diff --git a/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs b/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs index 14194b0a1070..ddb3da2dd95a 100644 --- a/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs +++ b/src/Mvc/Mvc.Testing/src/DeferredHostBuilder.cs @@ -1,10 +1,6 @@ // 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 System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -124,6 +120,8 @@ private class DeferredHost : IHost, IAsyncDisposable private readonly IHost _host; private readonly TaskCompletionSource _hostStartedTcs; + private bool _disposed; + public DeferredHost(IHost host, TaskCompletionSource hostStartedTcs) { _host = host; @@ -132,19 +130,24 @@ public DeferredHost(IHost host, TaskCompletionSource hostStartedTcs) public IServiceProvider Services => _host.Services; - public void Dispose() => _host.Dispose(); + public void Dispose() + { + if (!_disposed) + { + _host.Dispose(); + _disposed = true; + } + } public async ValueTask DisposeAsync() { if (_host is IAsyncDisposable disposable) { await disposable.DisposeAsync().ConfigureAwait(false); + _disposed = true; return; } - else - { - _host.Dispose(); - } + Dispose(); } public async Task StartAsync(CancellationToken cancellationToken = default)