-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Make ExceptionHandling.RaiseAppDomainUnhandledExceptionEvent()
thread safe.
#117844
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make ExceptionHandling.RaiseAppDomainUnhandledExceptionEvent()
thread safe.
#117844
Conversation
…I thread safe. The RaiseAppDomainUnhandledExceptionEvent() API is permitted to be called by multiple threads but only one should be triggering the event handlers.
There was a problem hiding this 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 adds thread safety to the ExceptionHandling.RaiseAppDomainUnhandledExceptionEvent()
method to ensure only one thread can trigger the unhandled exception event handlers while other threads wait for completion.
- Introduces a volatile static field to track exception handling state across threads
- Implements atomic operations with waiting mechanism to serialize event handler execution
- Adds test infrastructure using UnsafeAccessor to reset the static field after tests
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
File | Description |
---|---|
ExceptionHandling.cs | Adds thread synchronization logic using volatile field and atomic operations |
RaiseEvent.cs | Adds test helper using UnsafeAccessor to reset static state between tests |
src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs
Outdated
Show resolved
Hide resolved
src/tests/baseservices/exceptions/RaiseAppDomainUnhandledExceptionEvent/RaiseEvent.cs
Outdated
Show resolved
Hide resolved
Tagging subscribers to this area: @dotnet/interop-contrib |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Private.CoreLib/src/System/Runtime/ExceptionServices/ExceptionHandling.cs
Outdated
Show resolved
Hide resolved
It should stay. |
This comment was marked as outdated.
This comment was marked as outdated.
As far as I can tell, there are no unhandled exceptions in this test - the exceptions are handled in native code. It looks like a pre-existing bug to me that UnhandledException event is invoked in this test. @janvorli thoughts about this? |
Related #117620 |
Let me take a look why we invoke the handler. |
Currently, any managed exception that goes through a reverse pinvoke transition and there are no more managed frames on the stack is reported as unhandled, because we really cannot know whether it would be handled or not in the native caller. We invoke the As discussed in the #117620, we want to change that behavior to let the exception always flow into the external native calling code on Windows. However, there are some gotchas. Let me discuss those in the #117620 though. |
@AaronRobinsonMSFT I actually cannot repro the CI failures locally with your branch checked out. While the failing test reports the exception as unhandled via the event, it still passes for me. |
I believe the failure is only on Windows, the test passes for me too on non-Windows. I've not tried Windows yet. I think there might be some trickiness here based on the predicate for the failfast, it needs the same thread. Is it possible this is a flaky issue? Let me try triggering the CI and see if it fails again. |
@janvorli This hit the first time I ran it locally on Windows Debug.
|
@AaronRobinsonMSFT I wonder what's different for me. I have checked out your branch, built it on Windows x64 in Debug and yet I don't get the crash, neither with nor without debugger attached. I'll try to rebuild everything... |
I cannot repro it even after a full clean build of both the repo and tests |
Okay. It might have to wait until tomorrow, but what can I get for you? Full heap DMP? Anything else? |
Dump plus pdbs of the build would be great, thank you! |
@AaronRobinsonMSFT Sigh, my mistake. While I have pulled down your branch, by accident I had checked out another one. I am sorry for the confusion. |
The problem causing the failure is that the test really causes an unhandled exception twice on the same thread. The foreign thread catches the exception that runtime considers unhandled because it has flown to external native code with no more managed frames and then it calls a reverse pinvoke on the same thread and it throws again, causing second "unhandled" exception. |
Thus your logic thinks there was a recursion. To fix that on the EH side, the processing of exceptions that escape into foreign native code with no more managed frames on top of it will need to be changed so that they are no longer considered unhandled. |
The current behavior is a regression introduced by EH unification and follow up bug fixes. These exceptions should be considered handled - like it was in .NET Framework and in .NET Core before EH unification. Is this correct? |
Right. Although there will always be some differences in the EH due to the new EH not using SEH. The question will be which way of handling the differences is more acceptable than others. I have originally thought the concept of considering exceptions escaping last managed frame on a foreign thread unhandled from the managed code point of view was reasonable, but the recent issues related to that has proven me wrong. |
@AaronRobinsonMSFT I think this one is good to go in now. |
The
ExceptionHandling.RaiseAppDomainUnhandledExceptionEvent()
API is permitted to be called by multiple threads but only one should be triggering the event handlers.Fixes #117840