Share via


Thread.Abort() on a thread executing a Finally/Catch or .ctor block.

Issue: If a thread t is in some catch or finally or cctor block, calling t.Abort() from another thread does not have any effect on the thread. Here is the code to repro this.

 using System;using System.Threading;namespace ConsoleApplication2{class Program{static Thread t;[STAThread]static void Main(string[] args){Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);t = new Thread(ChildThread);t.Start();t.Join();}static void ChildThread(){try{throw new Exception();}catch (Exception){try{Console.WriteLine("going to sleep");Thread.Sleep(10000);Console.WriteLine("out of sleep");}catch (ThreadAbortException e){Console.WriteLine(e.ToString());}}}static void Console_CancelKeyPress(Object sender, ConsoleCancelEventArgs e){Console.WriteLine("aborting");if (t != null){t.Abort();t.Join();}}}}

Run the code and press CTRL+C to repro.

Club this issue with try\retry code in Abhinaba's blog and you have infinitly waiting call. May be this is the reason The Old New Thing advices against using retry indiscriminatly

Explaination that I got from the feature team:

Well, this is really interesting. Jeff's answer is a bit misleading; the actual behavior is more complex (and as the person who has to document it, I could wish it were more consistent).

(1) In the case of the catch block, the call to the Abort method does NOT block and the thread WILL abort as soon as the catch block completes. This is true even if no further code executes on the thread after the catch block.

(2) In the case of the finally block and the static constructor, the call to the Abort method appears to block until the finally block or static constructor completes! This is totally unexpected. If the thread doesn't execute any more code after the finally block or the static constructor, then ThreadAbortException is never thrown; but if the thread goes on executing long enough it WILL abort. (How long is "long enough"? Well, let's just say it's a race condition.)

Joe, can we say for sure that this is by design? I don't like the fact that the catch behaves differently from the finally and the static .ctor, and I REALLY don't like the Abort method blocking.

reply to this:

We should mention that Aborts called asynchronously on another thread might block, but I don’t know that we should document the exact behavior. Because there are situations where it won’t block, we do not want people to rely on blocking e.g. to signal that the thread has finished its abort.

But the more important (IMHO) thing to document is when abort requests are actually processed. Not processing the abort until after leaving catches, finallys, cctors, CERs, and unmanaged code is expected & fine. There are places we know of—e.g. Socket.Accept—which ends up blocking in an FCALL, which means we are unable to interrupt it. So this is an explanation of a bug that a user might reasonably hit.

What I don't like About this:

  1. As mentioned the call should not be blocking. This leaves the user waiting indefinitly. I have hit this bug making a webservice call and there was an IISREST on the server my call got stuck till there was a timeout at the client side and a Thread.Abort() on given thread was just waiting!!!
  2. There is no way to say Abort no matter what. So you may end up adding complex logic to have a clean exit. see my earlier blog.
  3. And to top that there is no documentation arround this. We would have been doomed if we were not in Microsoft. It was easy for us to just mail a discussion group and get a very quick response from the right people. Though they acknowledge this but I know its not an easy process to get the documentation updated after release. Just hope that they do it.

Comments

  • Anonymous
    November 30, 2005
    I was initially frightened after reading Explaination that I got from the feature team (2) but in my code, I don't have time intensive static constructors or finally blocks. The static constructors 99% of the time just initialize static readonly fields and the finally blocks are usually used for some type of cleanup and more importantly, I don't call Thread.Abort without the user's permission so if it hangs (blocking on Abort) and what not, it's the user's fault and I do warn them before attempting to abort threads. :)

    Good post though!
  • Anonymous
    November 30, 2005
    Well even I call Thread.Abort only on CTRL+C event but then there is some clean up to be done for that you need to ensure that objects you clean up are not being accessed in other threads or you'll end up with a NullReferenceException.
  • Anonymous
    November 30, 2005
    Also I have a catch block that keeps on retrying on network errors. I am working on a migration tool that runs for hours/days and cannot simply fail on transient network errors so I have an infinite retry. Now if user chooses to abort the application I need to do some clean up, generate migration reports, etc. Handling CTRL+C kind of scenario is vercy necessary for my product.
  • Anonymous
    December 01, 2005
    The comment has been removed