diff --git a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs index 56ce966d18..c2c9084e0a 100644 --- a/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs +++ b/src/HealthChecks/HealthChecks/src/DefaultHealthCheckService.cs @@ -54,7 +54,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks { foreach (var registration in registrations) { - tasks[index++] = RunCheckAsync(scope, registration, cancellationToken); + tasks[index++] = Task.Run(() => RunCheckAsync(scope, registration, cancellationToken), cancellationToken); } await Task.WhenAll(tasks).ConfigureAwait(false); @@ -75,8 +75,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks private async Task RunCheckAsync(IServiceScope scope, HealthCheckRegistration registration, CancellationToken cancellationToken) { - await Task.Yield(); - cancellationToken.ThrowIfCancellationRequested(); var healthCheck = registration.Factory(scope.ServiceProvider); @@ -104,7 +102,7 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks checkCancellationToken = timeoutCancellationTokenSource.Token; } - result = await healthCheck.CheckHealthAsync(context, checkCancellationToken); + result = await healthCheck.CheckHealthAsync(context, checkCancellationToken).ConfigureAwait(false); var duration = stopwatch.GetElapsedTime(); @@ -118,7 +116,6 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks Log.HealthCheckEnd(_logger, registration, entry, duration); Log.HealthCheckData(_logger, registration, entry); } - catch (OperationCanceledException ex) when (!cancellationToken.IsCancellationRequested) { var duration = stopwatch.GetElapsedTime(); diff --git a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs index ba0a2f32d5..38442edb93 100644 --- a/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs +++ b/src/HealthChecks/HealthChecks/test/DefaultHealthCheckServiceTest.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Testing; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Testing; using Microsoft.Extensions.Options; @@ -450,6 +451,39 @@ namespace Microsoft.Extensions.Diagnostics.HealthChecks }); } + [Fact] + public void CheckHealthAsync_WorksInSingleThreadedSyncContext() + { + // Arrange + var service = CreateHealthChecksService(b => + { + b.AddAsyncCheck("test", async () => + { + await Task.Delay(1).ConfigureAwait(false); + return HealthCheckResult.Healthy(); + }); + }); + + var hangs = true; + + // Act + using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3))) + { + var token = cts.Token; + token.Register(() => throw new OperationCanceledException(token)); + + SingleThreadedSynchronizationContext.Run(() => + { + // Act + service.CheckHealthAsync(token).GetAwaiter().GetResult(); + hangs = false; + }); + } + + // Assert + Assert.False(hangs); + } + private static DefaultHealthCheckService CreateHealthChecksService(Action configure) { var services = new ServiceCollection(); diff --git a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj index 163b618900..08cd6a35f1 100644 --- a/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj +++ b/src/HealthChecks/HealthChecks/test/Microsoft.Extensions.Diagnostics.HealthChecks.Tests.csproj @@ -12,4 +12,8 @@ + + + +