Func-eval abort is evil

Func-eval is evil. Func-eval abort is even worse. For those coming in late, Func-eval is when the debugger hijacks a thread and has it evaluate some function such as a property-getter or to-string.   Func-eval abort is when that evaluation hangs, and then the debugger aborts it (similar to Thread.Abort). 

Visual Studio sets up a timer when it does automatic func-evals, and will automatically issue an abort after a few seconds.

For example, say you have a very very evil property like the one below.

 
    class Evil
    {
        public string EvilProperty
        {
            get
            {
                Console.WriteLine("start");
                Thread.Sleep(1000 * 10);
                Console.WriteLine("done");
                return "finished!";
            }
        }
    }

If you allocate an Evil object in VS, and then view it in the watch window, VS will automatically func-eval the property getters (if func-eval is enabled). If the eval takes too long (which that Sleep will ensure in this case), it will abort the func-eval. It looks like this:

 

(screenshots courtesy of Windows Live Writer! )

ICorDebug?
For debugger authors implementing func-eval support, the corresponding APIs are ICorDebugEval::Abort and ICorDebugEval2::RudeAbort. Like most ICorDebug operations, these are asynchronous. Issue them, call Continue(), and then wait for an Abort Complete (EvalComplete/EvalException) event.

So why is it evil?

Some reasons why abort is evil and should not be a backbone of any debugging strategy:

  1. Abort may not always be possible. For example, the CLR can't abort a thread in native code. This can be a very common scenario for winforms properties that do a SendMessage.
  2. Abort has all the same problems of Thread.Abort and more. For example, it can leave the managed user state in an undefined position. For example, if you abort a thread in the middle of updating some user data structure (eg,a hash), that structure may then be left in an inconsistent state.
  3. At a abstract level, we all recognize that func-evals change program behavior; and aborts are even worse. Func-eval at least executes an entire function atomically. Abort executes a random subset of the function, and can thus have very significant unpredictable behavior changing affects.
  4. Rude-abort doesn’t run backout code, and may leave the thread in an undefined state. For example, it may leave locks orphaned causing strange deadlocks later on.
  5. If abort becomes very common, these previous issues may lead to a lot of “My code breaks under the debugger, it’s a debugger bug”.
  6. Use features the way they were intended. Abort was supposed to be a mitigation for stray func-evals, not a mainline scenario.