Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can ThreadAbortException skip finally?

Everything I've read claims an abort on a thread will execute the finally block before ending from a ThreadAbortException. I wanted to confirm this so I can plan on how to handle some 3rd party code that can hang indefinitely. However the following test has me confused:

public void runTest(DateTime deadline)
{
    testThread = new Thread(() => 
    {
        try 
        {
            Console.WriteLine("test thread started at " + DateTime.Now.ToShortTimeString());
            while (true) { }
        }
        finally
        {
            Console.WriteLine("test thread entered FINALLY at " + DateTime.Now.ToShortTimeString());
            while (true) { }
        }
    });
    testThread.Start();
    while (testThread.IsAlive && deadline.Subtract(DateTime.Now).TotalSeconds > 0)
    {
        Console.WriteLine("main thread while loop " + DateTime.Now.ToShortTimeString());
        Thread.Sleep(10000);
    }
    if (testThread.IsAlive)
        testThread.Abort();
    Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());
}

What I find when running this is that console never mentions entering the finally block. The application continues on after the .abort call as if there is no finally block at all. Am I doing something wrong? Shouldn't control pass to the finally block before reaching the final write to console or is the execution order still a function of the fact that the finally is in a separate thread or something?

like image 606
user1807768 Avatar asked Aug 01 '13 19:08

user1807768


2 Answers

Docs say: ThreadAbortException is a special exception that can be caught, but it will automatically be raised again at the end of the catch block. When this exception is raised, the runtime executes all the finally blocks before ending the thread. Because the thread can do an unbounded computation in the finally blocks or call Thread.ResetAbort to cancel the abort, there is no guarantee that the thread will ever end.

I'm pretty sure your thread is being dumped because you exit the method and lose a reference to it, and so it gets collected by the Garbage Collector. Try making the testThread variable a field member of the class and see what happens.

That, or you have a race condition since the threads run in parallel: the main thread is finishing before the spun-up test thread can output the finally data (Exceptions are expensive and take time to reach catch or finally blocks).

like image 51
Haney Avatar answered Sep 30 '22 05:09

Haney


The finally block in the worker thread function is executed on the worker thread which is parallel to the main thread. It's a race condition. You can not tell which of worker thread finally or main thread code after abort call get executed sooner. If you need a synchronous abort then you have to put something like that:

        if (testThread.IsAlive)
        {
            testThread.Abort();

            bool blnFinishedAfterAbort = testThread.Join(TimeSpan.FromMilliseconds(1000));
            if (!blnFinishedAfterAbort)
            {
                Console.WriteLine("Thread abort failed.");
            }
        }
        Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());

Keep in mind that if you have legacy exception handling enabled (see http://msdn.microsoft.com/en-us/library/ms228965.aspx) and you specified the AppDomain_UnahandledException event handler then the ThreadAbortException will lead execution to that handler BEFORE the finally block in the worker thread function. This is just another example of frustrating and unexpected execution order one should be aware of while aborting threads.

like image 45
Zverev Evgeniy Avatar answered Sep 30 '22 05:09

Zverev Evgeniy