Ensure BackgroundService invokes ExecuteAsync after start - #132241
Open
steveisok wants to merge 1 commit into
Open
Ensure BackgroundService invokes ExecuteAsync after start#132241steveisok wants to merge 1 commit into
steveisok wants to merge 1 commit into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 3dfd3a15-eb10-455b-8c1c-16ecd87fd841
|
Azure Pipelines: Successfully started running 3 pipeline(s). 13 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Member
Author
|
@jeffhandley deferring to you on who best to review. |
Contributor
There was a problem hiding this comment.
Pull request overview
This PR adjusts BackgroundService.StartAsync scheduling so that ExecuteAsync is still invoked even if the service is stopped/disposed immediately after start, and adds regression coverage to validate the behavior under deterministic thread-pool starvation.
Changes:
- Update
BackgroundService.StartAsyncto avoid using the stopping token as theTask.Runscheduling token. - Add a regression test that blocks the sole thread-pool worker and verifies
ExecuteAsyncstill runs exactly once when immediately stopped/disposed. - Update the pre-canceled
StartAsynctest to assertExecuteTaskis canceled andExecuteAsyncis not invoked.
Show a summary per file
| File | Description |
|---|---|
| src/libraries/Microsoft.Extensions.Hosting.Abstractions/src/BackgroundService.cs | Changes how ExecuteAsync is scheduled/canceled to prevent cancellation from suppressing delegate invocation. |
| src/libraries/Microsoft.Extensions.Hosting/tests/UnitTests/BackgroundServiceTests.cs | Adds deterministic regression coverage for immediate stop/dispose and refines pre-canceled start assertions. |
Review details
- Files reviewed: 2/2 changed files
- Comments generated: 2
- Review effort level: Lite
Comment on lines
45
to
+48
| // Execute all of ExecuteAsync asynchronously, and store the task we're executing so that we can wait for it later. | ||
| _executeTask = Task.Run(() => ExecuteAsync(_stoppingCts.Token), _stoppingCts.Token); | ||
| _executeTask = cancellationToken.IsCancellationRequested | ||
| ? Task.FromCanceled(cancellationToken) | ||
| : Task.Run(() => ExecuteAsync(_stoppingCts.Token), CancellationToken.None); |
Comment on lines
152
to
+154
|
|
||
| [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] | ||
| [InlineData(false)] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #131249.
BackgroundService.StartAsyncpassed its stopping token toTask.Runas the scheduling token. IfStopAsyncorDisposecanceled that token before the queued delegate began, the task transitioned toCanceledwithout invokingExecuteAsync.This change:
ExecuteAsyncwork runs asynchronously on a thread-pool thread.CancellationToken.Nonefor scheduling so cancellation cannot suppress delegate invocation after a non-canceled start is accepted.StartAsyncbehavior by explicitly assigningTask.FromCanceled(cancellationToken)toExecuteTask.Regression tests deterministically occupy the sole thread-pool worker and verify that immediate stop and dispose still invoke
ExecuteAsyncexactly once on a thread-pool thread with an already-canceled stopping token. Pre-canceled startup coverage also verifies thatExecuteAsyncis not invoked.Note
This pull request description was generated with GitHub Copilot.