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:
ThreadAbortExceptionis a special exception that can be caught, but it will automatically be raised again at the end of thecatchblock.
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?