Home Why isn't ThreadAbortException automatically rethrown if raised after an asynchronous await?
Reply: 1

Why isn't ThreadAbortException automatically rethrown if raised after an asynchronous await?

Michael Liu
1#
Michael Liu Published in 2018-01-12 15:59:03Z

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?

Stefano d'Antonio
2#
Stefano d'Antonio Reply to 2018-01-12 16:57:57Z

The reason why it happens if you don't await Task.Yield is that the code is executed synchronously on the same thread as the caller so it's like not being async at all.

When you await, the continuation will be queued on a ThreadPool thread 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:

If any of these exceptions are unhandled in threads created by the common language runtime, the exception terminates the thread, but the common language runtime does not allow the exception to proceed further.

If these exceptions are unhandled in the main thread, or in threads that entered the runtime from unmanaged code, they proceed normally, resulting in termination of the application.

My guess on the rationale behind this is: ThreadAbortException is 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.

You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.33181 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO