Description
Description
Unlike every other uncaught exception in CoreCLR, an uncaught ThreadInterruptedException
exits the process without printing an "unhandled exception" message to the console.
Reproduction Steps
Create a console program with dotnet new console
. Replace the contents of Program.cs
with the following:
using System.Threading;
throw new ThreadInterruptedException();
Run.
Expected behavior
I would expect a message to be printing to the console that looks roughly like this:
Unhandled exception. System.Threading.ThreadInterruptedException: Thread was interrupted from a waiting state.
at Program.<Main>$(String[] args) in C:\temp\interrupt-test\Program.cs:line 2
Actual behavior
Nothing is printed. On unix-like operating systems, the shell says "aborted (core dumped)". Note that the "core dumped" message is only visible if the built executable is run directly, not if run with dotnet run
.
Regression?
No, this reproduces in .NET 8 and .NET 6. .NET Framework 4.8.1 also has this behavior.
Known Workarounds
This exception can be caught with a normal exception handler. The ability to catch this exception is not a problem.
Configuration
This behavior reproduces with .NET 8.0.8 on the following systems:
- AMD 64 Windows
- AMD 64 Linux (Ubuntu 22.04 physical and 24.04 WSL)
- ARM 64 macOS
Other information
I ran the reproducer program on HEAD of this repo to gather some stack traces. The system was a X64 Linux system. The problem reproduces both with the new and old exception handling.
DOTNET_LegacyExceptionHandling=1
Native stack trace:
* frame #0: 0x00007ffff78969fc libc.so.6`__GI___pthread_kill at pthread_kill.c:44:76
frame #1: 0x00007ffff78969b0 libc.so.6`__GI___pthread_kill [inlined] __pthread_kill_internal(signo=6, threadid=140737352611648) at pthread_kill.c:78:10
frame #2: 0x00007ffff78969b0 libc.so.6`__GI___pthread_kill(threadid=140737352611648, signo=6) at pthread_kill.c:89:10
frame #3: 0x00007ffff7842476 libc.so.6`__GI_raise(sig=6) at raise.c:26:13
frame #4: 0x00007ffff78287f3 libc.so.6`__GI_abort at abort.c:79:7
frame #5: 0x00007ffff76ceed3 libcoreclr.so`::PROCAbort(signal=6, siginfo=0x0000000000000000) at process.cpp:2562:5
frame #6: 0x00007ffff76ceb5e libcoreclr.so`PROCEndProcess(hProcess=0x00000000ffffff01, uExitCode=1, bTerminateUnconditionally=YES) at process.cpp:1354:13
frame #7: 0x00007ffff76cec30 libcoreclr.so`::TerminateProcess(hProcess=0x00000000ffffff01, uExitCode=1) at process.cpp:1251:11
frame #8: 0x00007ffff6ca339b libcoreclr.so`CrashDumpAndTerminateProcess(exitCode=1) at excep.cpp:3962:5
frame #9: 0x00007ffff70892c5 libcoreclr.so`UnwindManagedExceptionPass1(ex=0x00007fffffffc380, frameContext=0x00007fffffffb570) at exceptionhandling.cpp:4967:17
frame #10: 0x00007ffff70898ec libcoreclr.so`DispatchManagedException(ex=0x00007fffffffc380, isHardwareException=false) at exceptionhandling.cpp:5052:17
frame #11: 0x00007ffff6f3ee52 libcoreclr.so`IL_Throw(obj=0x00007fbf5e80a9e8) at jithelpers.cpp:3052:5
frame #12: 0x00007fff79121d4a
frame #13: 0x00007ffff71e2a14 libcoreclr.so`CallDescrWorkerInternal at calldescrworkeramd64.S:70
frame #14: 0x00007ffff6e663d5 libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffffc7d8, fCriticalCall=NO) at callhelpers.cpp:63:5
frame #15: 0x00007ffff6e670cc libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=0x00007fffffffc900, pArguments=0x00007fffffffc8a8, pReturnValue=0x0000000000000000, cbReturnValue=0) at callhelpers.cpp:585:9
frame #16: 0x00007ffff6bd3e53 libcoreclr.so`MethodDescCallSite::Call(this=0x00007fffffffc900, pArguments=0x00007fffffffc8a8) at callhelpers.h:465:9
frame #17: 0x00007ffff6bf5fdf libcoreclr.so`RunMainInternal(pParam=0x00007fffffffcb60) at assembly.cpp:1211:21
.NET stack trace:
(lldb) sos clrstack
OS Thread Id: 0x23b92 (1)
Child SP IP Call Site
00007FFFFFFFC3C8 00007ffff78969fc [HelperMethodFrame: 00007fffffffc3c8]
00007FFFFFFFC4D0 00007FFF79121D4A Program.<Main>$(System.String[])
DOTNET_LegacyExceptionHandling=0
Native stack trace:
* frame #0: 0x00007ffff78969fc libc.so.6`__GI___pthread_kill at pthread_kill.c:44:76
frame #1: 0x00007ffff78969b0 libc.so.6`__GI___pthread_kill [inlined] __pthread_kill_internal(signo=6, threadid=140737352611648) at pthread_kill.c:78:10
frame #2: 0x00007ffff78969b0 libc.so.6`__GI___pthread_kill(threadid=140737352611648, signo=6) at pthread_kill.c:89:10
frame #3: 0x00007ffff7842476 libc.so.6`__GI_raise(sig=6) at raise.c:26:13
frame #4: 0x00007ffff78287f3 libc.so.6`__GI_abort at abort.c:79:7
frame #5: 0x00007ffff76ceed3 libcoreclr.so`::PROCAbort(signal=6, siginfo=0x0000000000000000) at process.cpp:2562:5
frame #6: 0x00007ffff76ceb5e libcoreclr.so`PROCEndProcess(hProcess=0x00000000ffffff01, uExitCode=3762504530, bTerminateUnconditionally=YES) at process.cpp:1354:13
frame #7: 0x00007ffff76cec30 libcoreclr.so`::TerminateProcess(hProcess=0x00000000ffffff01, uExitCode=3762504530) at process.cpp:1251:11
frame #8: 0x00007ffff6ca339b libcoreclr.so`CrashDumpAndTerminateProcess(exitCode=3762504530) at excep.cpp:3962:5
frame #9: 0x00007ffff7092bd2 libcoreclr.so`::SfiNext(pThis=0x00007fffffff8940, uExCollideClauseIdx=0x00007fffffff85b0, fUnwoundReversePInvoke=0x00007fffffff85c0, pfIsExceptionIntercepted=0x00007fffffff85d0) at exceptionhandling.cpp:8580:21
frame #10: 0x00007fff77bc4e9b
frame #11: 0x00007fff77b9844b
frame #12: 0x00007fff77b97e5c
frame #13: 0x00007ffff71e2a14 libcoreclr.so`CallDescrWorkerInternal at calldescrworkeramd64.S:70
frame #14: 0x00007ffff6e663d5 libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffff8708, fCriticalCall=YES) at callhelpers.cpp:63:5
frame #15: 0x00007ffff6e66825 libcoreclr.so`DispatchCallSimple(pSrc=0x00007fffffff8810, numStackSlotsToCopy=0, pTargetAddress=140735228741672, dwDispatchCallSimpleFlags=1) at callhelpers.cpp:245:9
frame #16: 0x00007ffff7089e19 libcoreclr.so`DispatchManagedException(throwable=OBJECTREF @ 0x00007fffffffb528, pExceptionContext=0x00007fffffffb530) at exceptionhandling.cpp:5697:5
frame #17: 0x00007ffff708a784 libcoreclr.so`DispatchManagedException(throwable=OBJECTREF @ 0x00007fffffffc178) at exceptionhandling.cpp:5714:5
frame #18: 0x00007ffff6f3e68f libcoreclr.so`ThrowNew(oref=OBJECTREF @ 0x00007fffffffc370) at jithelpers.cpp:2994:5
frame #19: 0x00007ffff6f3e91c libcoreclr.so`IL_Throw(obj=0x00007fbf5e80a9e8) at jithelpers.cpp:3019:9
frame #20: 0x00007fff79111d4a
frame #21: 0x00007ffff71e2a14 libcoreclr.so`CallDescrWorkerInternal at calldescrworkeramd64.S:70
frame #22: 0x00007ffff6e663d5 libcoreclr.so`CallDescrWorkerWithHandler(pCallDescrData=0x00007fffffffc7d8, fCriticalCall=NO) at callhelpers.cpp:63:5
frame #23: 0x00007ffff6e670cc libcoreclr.so`MethodDescCallSite::CallTargetWorker(this=0x00007fffffffc900, pArguments=0x00007fffffffc8a8, pReturnValue=0x0000000000000000, cbReturnValue=0) at callhelpers.cpp:585:9
frame #24: 0x00007ffff6bd3e53 libcoreclr.so`MethodDescCallSite::Call(this=0x00007fffffffc900, pArguments=0x00007fffffffc8a8) at callhelpers.h:465:9
frame #25: 0x00007ffff6bf5fdf libcoreclr.so`RunMainInternal(pParam=0x00007fffffffcb60) at assembly.cpp:1211:21
.NET Stack trace:
(lldb) sos clrstack
OS Thread Id: 0x23cd3 (1)
Child SP IP Call Site
00007FFFFFFF8490 00007ffff78969fc [InlinedCallFrame: 00007fffffff8490]
00007FFFFFFF8490 00007fff77bc4e76 [InlinedCallFrame: 00007fffffff8490]
00007FFFFFFF8460 00007FFF77BC4E76 System.Runtime.ExceptionServices.InternalCalls.RhpSfiNext(System.Runtime.StackFrameIterator ByRef, UInt32*, Boolean*, Boolean*)
00007FFFFFFF8520 00007FFF77B9844B System.Runtime.EH.DispatchEx(System.Runtime.StackFrameIterator ByRef, ExInfo ByRef)
00007FFFFFFF8630 00007FFF77B97E5C System.Runtime.EH.RhThrowEx(System.Object, ExInfo ByRef)
00007FFFFFFFC3C8 00007ffff71e2a14 [HelperMethodFrame: 00007fffffffc3c8]
00007FFFFFFFC4D0 00007FFF79111D4A Program.<Main>$(System.String[])
It looks like that process is being aborted because the exception is being treated like an unhanded exception escaping from a reverse P/Invoke.
Looking at both code paths, I'm guessing this code might be responsible for the lack of message:
runtime/src/coreclr/vm/excep.cpp
Lines 5023 to 5028 in 7ae87de
It seems like it is intentional to print nothing on async exceptions?
For completeness, this also prints nothing when unhandled:
throw (Exception)Activator.CreateInstance(typeof(ThreadAbortException), nonPublic: true)!;
And this shows that runtime thrown exceptions also print nothing when unhandled:
Thread.CurrentThread.Interrupt();
Thread.Sleep(1);