As a result of investigating this question: Rethrowing exception in Task doesn't make the Task to go to faulted state, I noticed some very odd behavior with the ThreadAbortException
that I can't make sense of.
Now, I know that ThreadAbortException
is a very special kind of exception to begin with. And the documentation is pretty clear about that when it says:
ThreadAbortException
is a special exception that can be caught, but it will automatically be raised again at the end of thecatch
block.
Scenario #1: Documented behavior.
static void Main(string[] args)
{
try
{
Thread.CurrentThread.Abort();
}
catch (Exception tae)
{
Console.WriteLine("caught exception: " + tae.GetType().Name);
}
Console.WriteLine("will never be reached");
}
As expected, the ThreadAbortException
is rethrown automatically, resulting in the following output:
caught exception: ThreadAbortException
Scenario #2: Where it gets interesting is when I decide to throw a different exception in the catch
block:
static void Main(string[] args)
{
try
{
Thread.CurrentThread.Abort();
}
catch (Exception tae)
{
Console.WriteLine("caught exception: " + tae.GetType().Name);
throw new ApplicationException(); // will ThreadAbortException take precedence?
}
Console.WriteLine("will never be reached");
}
In this case, I assumed that, despite the throwing of the ApplicationException
, that the ThreadAbortException
would be rethrown anyways to ensure that the documented behavior was preserved. To my surprise, this is the output that resulted:
caught exception: ThreadAbortException
Unhandled Exception: System.ApplicationException: Error in the application.
at ConsoleApplication1.Program.Main(String[] args) in C:\projects\ConsoleApplication1\Program.cs:line 193
Did the ApplicationException
actually replace and prevent the ThreadAbortException
from being thrown?!?
Scenario #3: And finally, to make matters more interesting, I wrap my existing exception handling with one more try-catch
layer:
static void Main(string[] args)
{
try
{
try
{
Thread.CurrentThread.Abort();
}
catch (Exception tae)
{
Console.WriteLine("caught exception: " + tae.GetType().Name);
throw new ApplicationException();
}
}
catch (Exception outerEx)
{
Console.WriteLine("caught outer exception: " + outerEx.GetType().Name);
}
Console.WriteLine("will never be reached");
}
Now I'm not too sure what to expect. Which exception will be caught in the outer catch
block? Will it be ApplicationException
? And if so, does that mean that I'll be able to swallow the exception and actually manage to print out the will never be reached
string after all?
This is the actual output:
caught exception: ThreadAbortException
caught outer exception: ApplicationException
From the above output, it looks like the outer catch
block does actually catch an ApplicationException
. But by the end of that catch
block, the ThreadAbortException
now all of a sudden reappears out of thin air and gets rethrown?
Question(s): Can someone explain and reconcile scenarios #2 and #3? How is it that in scenario #2, it looks as though the ThreadAbortException
is, unexpectedly, replaced by a different exception? Yet, in scenario #3, it looks like ThreadAbortException
was still there all along? How is this happening? Is this behavior documented somewhere?