Skip to content

Fix race condition in BackgroundServiceExceptionTests - #125570

Merged
svick merged 4 commits into
dotnet:mainfrom
lewing:fix/backgroundservice-test-race
Mar 17, 2026
Merged

Fix race condition in BackgroundServiceExceptionTests#125570
svick merged 4 commits into
dotnet:mainfrom
lewing:fix/backgroundservice-test-race

Conversation

@lewing

@lewinglewing commented Mar 15, 2026

Copy link
Copy Markdown
Member

Problem

BackgroundService_AsynchronousException_StopAsync_ThrowsException is flaky (3 hits in 24 hours, #125550).

The test starts a BackgroundService that delays 100ms then throws, waits Task.Delay(200ms), and expects StopAsync to surface the exception. On loaded CI agents the 200ms isn't always enough for the exception to propagate through the host, so Assert.Throws sees no exception.

Assert.Throws() Failure: No exception was thrown
Expected: typeof(System.InvalidOperationException)

Fix

Replace the racy Task.Delay(200ms) with deterministic synchronization:

For StopHost behavior tests (StopAsync_ThrowsException, StopTwiceAsync_ThrowsException): Wait for IHostApplicationLifetime.ApplicationStopping, which fires after the host has captured the faulted service exception and called StopApplication(). This is the correct signal because it means the host has processed the exception and is ready to surface it on StopAsync.

For Ignore behavior test (IgnoreException_StopAsync_DoesNotThrow): Use a SignalingFailureService that signals a TaskCompletionSource before throwing. Since this test verifies StopAsync does not throw, we just need to know the service has faulted — no host-level processing is needed.

Both use a 10-second timeout as a safety net for CI.

Fixes#125550

The StopAsync and StopTwiceAsync tests used Task.Delay(200ms) to wait
for a background service (which delays 100ms then throws) to fault.
On loaded CI agents, 200ms is not always enough for the exception to
propagate through the host, causing Assert.Throws to see no exception.
For StopHost behavior tests: wait for ApplicationStopping, which fires
after the host has captured the exception and called StopApplication().
For Ignore behavior test: use a SignalingFailureService that signals a
TaskCompletionSource before throwing, so the test knows the service
has faulted without relying on timing.
Fixesdotnet#125550
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings March 15, 2026 01:58
@github-actionsgithub-actionsBot added the area-ExceptionHandling-coreclr only use for closed issues label Mar 15, 2026
@lewing
lewing requested a review from svickMarch 15, 2026 02:01

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Improves reliability of Microsoft.Extensions.Hosting unit tests by replacing timing-based delays with deterministic synchronization points when validating background service exception handling.

Changes:

  • Replaced racy Task.Delay(...) waits with IHostApplicationLifetime.ApplicationStopping signaling for StopHost behavior tests.
  • Updated the Ignore behavior test to use a signaling background service (SignalingFailureService) to deterministically coordinate when the failure path is reached.
  • Added 10s WaitAsync timeouts as CI safety nets.

You can also share your feedback on Copilot code review. Take the survey.

@lewinglewing assigned svick and unassigned lewingMar 15, 2026
@lewing
lewing requested a review from cincuranetMarch 15, 2026 02:03
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lewinglewing added area-Extensions-Hosting and removed area-ExceptionHandling-coreclr only use for closed issues labels Mar 15, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-extensions-hosting
See info in area-owners.md if you want to be subscribed.

CopilotAI review requested due to automatic review settings March 16, 2026 12:59

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reduces flakiness in Microsoft.Extensions.Hosting unit tests by replacing a timing-based wait with a deterministic signal that the host has begun stopping after a background service fault, so StopAsync reliably surfaces the captured exception.

Changes:

  • Replaced Task.Delay(200ms) with waiting on IHostApplicationLifetime.ApplicationStopping (with a 10s timeout) for StopHost-behavior tests.
  • Updated comments in the Ignore-behavior StopAsync test (but the test still uses a fixed delay).

@svick

Copy link
Copy Markdown
Member

/ba-g All failures seem to be iOS specific and not related to this PR.

@svick
svick merged commit 8d88e2b into dotnet:mainMar 17, 2026
89 of 95 checks passed
@github-actionsgithub-actionsBot locked and limited conversation to collaborators Apr 16, 2026
Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Microsoft.Extensions.Hosting.Tests.BackgroundServiceExceptionTests.BackgroundService_AsynchronousException_StopAsync_ThrowsException failed

4 participants

@lewing@svick@rosebyte