'C# `ThreadAbortException` is not actually rethrown, how does it work?

It's a long standing piece of .NET trivia that:

ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.

My hypothesis was always that it's just a special runtime thing, where it will just forcefully insert a throw if it ever catches ThreadAbortException.

Now this exception is special in that it has no public constructors, it is only constructed by the Thread.Abort method. And Thread.Abort is not implemented for .NET Core and later, it only works for .NET Framework. Therefore to actually get a ThreadAbortException on newest .NET installs we need reflection trickery, to get access to the internal parameterless constructor:

var instance = (ThreadAbortException)Activator.CreateInstance(
    typeof(ThreadAbortException),
    BindingFlags.Instance | BindingFlags.NonPublic,
    null,
    null,
    null
);

But, lo and behold, it doesn't work as advertised!

try
{
    throw instance;
}
catch
{
    Console.WriteLine("Caught!");
}

Console.WriteLine("Continuing...");

This just prints:

> Caught!
> Continuing...

And successfully completes with exit code 0. (SharpLab sample)

My thought was, okay, if newest .NET doesn't have the Thread.Abort stuff, then it might not have the bit responsible for propagating ThreadAbortException. So let's recompile that same code under .NET Framework 4.8 and run it.

> Caught!
> Continuing...

And now I'm stumped.

Obviously using reflection is cheating and the exception and Thread.Abort were designed with the assumption that you can't create an instance of ThreadAbortException from the outside. That much is clear. However, now I have no idea how this might work. If it was the case that "ThreadAbortException is magic", then I can envision a piece of runtime code that just says

if (caughtException type is ThreadAbortException)
{
    inject throw code at exit from catch block
}

But it clearly can't be it, because ThreadAbortException is not magic. Two questions:

  1. How does the automatic-rethrowing mechanism work if it doesn't look at the exception type?
  2. Why do the docs say that ThreadAbortException is automatically raised again at the end of the catch block if it's false?


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source