Skip to content

Catch exceptions thrown by 'async void' methods.#5068

Merged
manfred-brands merged 4 commits intomainfrom
Issue3930_AsyncVoid
Dec 6, 2025
Merged

Catch exceptions thrown by 'async void' methods.#5068
manfred-brands merged 4 commits intomainfrom
Issue3930_AsyncVoid

Conversation

@manfred-brands
Copy link
Copy Markdown
Member

@manfred-brands manfred-brands commented Nov 30, 2025

Fixes #3930
Fixes #3740

Also set exception handlers of last resort

Also set exception handlers of last resort
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements exception handling for async void methods and other unobserved exceptions during test execution. The changes ensure that exceptions thrown asynchronously that would otherwise crash the test runner are captured and reported as test failures.

Key changes:

  • Added a SafeSynchronizationContext wrapper to catch exceptions posted from async void methods
  • Registered global exception handlers (AppDomain.UnhandledException and TaskScheduler.UnobservedTaskException) to catch unobserved exceptions
  • Added test coverage for the async void exception handling scenario

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.

File Description
src/NUnitFramework/tests/AsyncVoidTests.cs New test verifying that exceptions from async void methods are caught and reported
src/NUnitFramework/testdata/AsyncVoidFixture.cs Test fixture demonstrating async void exception scenario with intentional delay
src/NUnitFramework/framework/Internal/ContextUtils.cs Implements SafeSynchronizationContext to catch exceptions posted by async void methods
src/NUnitFramework/framework/Api/NUnitTestAssemblyRunner.cs Registers and unregisters global exception handlers for unobserved exceptions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +78 to +79
context.CurrentResult.RecordException(e, FailureSite.Test);
context.CurrentResult.RecordTestCompletion();
Copy link

Copilot AI Nov 30, 2025

Choose a reason for hiding this comment

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

RecordException and RecordTestCompletion are called without synchronization. If multiple async void methods throw exceptions concurrently, this could result in race conditions when updating the test result state.

Copilot uses AI. Check for mistakes.
@manfred-brands
Copy link
Copy Markdown
Member Author

Currently the specific Windows SynchronizationContext tests are failing because of the extra unexpected context. I will look in them when I get time.

@OsirisTerje
Copy link
Copy Markdown
Member

@manfred-brands

Is this WIP ?

Or can this be merged in "as is" ?

I have no comments on it.

@manfred-brands
Copy link
Copy Markdown
Member Author

No, I fixed the code so the test all pass again.

@OsirisTerje OsirisTerje self-requested a review December 5, 2025 21:27
@manfred-brands manfred-brands merged commit 3669587 into main Dec 6, 2025
5 checks passed
@manfred-brands manfred-brands deleted the Issue3930_AsyncVoid branch December 6, 2025 01:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Whole tests sequence is aborted when single test fails because of async bug SynchronizationContext not preserved from OneTimeSetup to Test method.

3 participants