While investigating an issue with finally, await, and ThreadAbortException, I came another quirk. According to the documentation:
ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block.
But consider this console program:
class Program
{
static void Main()
{
Run(false).GetAwaiter().GetResult();
Run(true).GetAwaiter().GetResult();
}
static async Task Run(bool yield)
{
Console.WriteLine(yield ? "With yielding" : "Without yielding");
try
{
try { await Abort(yield); }
catch (ThreadAbortException)
{
Console.WriteLine(" ThreadAbortException caught");
} // <-- ThreadAbortException should be automatically rethrown here
}
catch (ThreadAbortException)
{
Console.WriteLine(" Rethrown ThreadAbortException caught");
Thread.ResetAbort();
}
}
static async Task Abort(bool yield)
{
if (yield)
await Task.Yield();
Thread.CurrentThread.Abort();
}
}
When I compile this in Visual Studio 2015, the output is:
Without yielding
ThreadAbortException caught
Rethrown ThreadAbortException caught
With yielding
ThreadAbortException caught
So a ThreadAbortException raised after Task.Yield() is no longer automatically rethrown by a catch block! Why is this?
The reason why it happens if you don't
await Task.Yieldis that the code is executed synchronously on the same thread as the caller so it's like not beingasyncat all.When you
await, the continuation will be queued on aThreadPoolthread which is a managed thread and behaves differently.Since internally is caught and re-thrown from a different thread than the current, it doesn't preserve it's nature of "special application killing" exception in the shifting logic.
In addition, if it was to re-throw it, you wouldn't even been able to
Thread.ResetAbort()as it works on the current thread and will not act on the one that actually aborted.MSDN documentation explains this, too here:
My guess on the rationale behind this is:
ThreadAbortExceptionis re-thrown to avoid stubborn threads to try and stay alive when they shouldn't, but letting it flow and kill also other different threads would probably be quite a bad idea and cause unexpected behaviour.