none
Debugger hangs with anonymous method invocation. RRS feed

  • Question

  • Hi,

    the following code, makes the debugger hang.  I have tried using begininvoke as well as invoke.  I get somewhat of a different behavior in VS 2008.  When I call the endinvoke the debugger, picks up on a unhandled exception.  When I call via invoke the exception is caught but the debugger is hung.

    Any thoughts?

    thanks

    using System;
    using System.Threading;
    
    namespace ConsoleApplication2
    {
        class TestException : Exception
        {
            public TestException()
                : base()
            {
            }
    
            public TestException(string message)
                : base(message)
            {
            }
        }
    
        class Program
        {
            delegate void TestDelegate();
    
            static void Main(string[] args)
            {
                Console.WriteLine("Main Thread id = {0}", Thread.CurrentThread.ManagedThreadId);
    
                TestDelegate e = delegate()
                {
                    Console.WriteLine("Thread id = {0}", Thread.CurrentThread.ManagedThreadId);
                    throw new TestException("My exception");
                };
    
                Run2(e);
            }
    
            private static void Run1(TestDelegate d)
            {
                AsyncCallback ac = delegate(IAsyncResult ar)
                {
                    d.EndInvoke(ar);
                };
    
                WaitCallback w = delegate(object o)
                {
                    try
                    {
                        d.BeginInvoke(ac, d);
                    }
                    catch (TestException ex)
                    {
                        Console.WriteLine("Exception caught");
                        Console.WriteLine("Exception message {0}", ex.Message);
                    }
                };
    
                ThreadPool.QueueUserWorkItem(w, new object());
            }
    
            private static void Run2(TestDelegate d)
            {
                WaitCallback w = delegate(object o)
                {
                    try
                    {
                        d();
                    }
                    catch (TestException ex)
                    {
                        Console.WriteLine("Exception caught");
                        Console.WriteLine("Exception message {0}", ex.Message);
                    }
                };
    
                ThreadPool.QueueUserWorkItem(w, new object());
            }
        }
    }
    



    hank voight
    Tuesday, April 21, 2009 3:59 PM

Answers

  • I dunno, why does your debugger stop at all?  Go into Debug + Exceptions and check the Thrown flag on CLR exceptions.  When I turn that on, I get 3 breaks.  Twice on the throw new Exception("My exception") statement and once on d.EndInvoke() call.  The latter one is normal, EndInvoke() marshals the exception from the threadpool thread started by d.BeginInvoke() to the threadpool thread started by QUWI and re-throws it.  There's no marshalling needed in Run2(), the thread that raised the exception is also the one that catches it.

    Of course, you should never write code like this.  There's no point in having one thread start another and then wait for the result.
    Hans Passant.
    • Marked as answer by hank voight Tuesday, April 21, 2009 6:54 PM
    Tuesday, April 21, 2009 6:26 PM
    Moderator

All replies

  • I can't repro, same version of VS.  You do need something like Console.ReadLine() after the Run2() call to prevent the main thread from exiting.  Output is:

    Main Thread id = 9
    Thread id = 6
    Exception caught
    Exception message My exception

    Note that your code is technically incorrect.  You are getting into trouble with anonymous methods.  It is the EndInvoke() method that re-raises the exception.  You should thus write it like this:

        private static void Run1(TestDelegate d) {
          AsyncCallback ac = delegate(IAsyncResult ar) {
            try {
              d.EndInvoke(ar);
            }
            catch (TestException ex) {
              Console.WriteLine("Exception caught");
              Console.WriteLine("Exception message {0}", ex.Message);
            }
          };

          WaitCallback w = delegate(object o) {
              d.BeginInvoke(ac, d);
          };

          ThreadPool.QueueUserWorkItem(w, new object());
        }

    Hans Passant.
    Tuesday, April 21, 2009 4:32 PM
    Moderator
  • good catch on the Run1...

    But I still get different behaviors....

    In the Run1 method above the debugger stops and says "Unhandled exception by user code" when the exception is thrown but in the run2 method I get no complaints.  Must be a difference in the implementation of Invoke vs. BeginInvoke().

    I implemented a readline method after the call to Run() in Main() and the debugger behaved as expected.  with a breakpoint in lieu of the readline, things appeared to be hung. 

    And you don't get that behavior?
    hank voight
    Tuesday, April 21, 2009 5:23 PM
  • Why do you expect an exception in Run2()?  There's a catch handler in Run1().  If you used a breakpoint before to stop the main thread, you will also break all other threads.  It isn't hung, it is broken :)
    Hans Passant.
    Tuesday, April 21, 2009 5:28 PM
    Moderator
  • for clarity both run1 and run2 have catch handlers.  They both catch the exception...  However the debugger stops on the exception thrown when Run1 is implemented but not in Run2. 
    private static void Run1(TestDelegate d) {
          AsyncCallback ac = delegate(IAsyncResult ar) {
            try {
              d.EndInvoke(ar);
            }
            catch (TestException ex) {
              Console.WriteLine("Exception caught");
              Console.WriteLine("Exception message {0}", ex.Message);
            }
          };

          WaitCallback w = delegate(object o) {
              d.BeginInvoke(ac, d);
          };

          ThreadPool.QueueUserWorkItem(w, new object());
        }





    private static void Run2(TestDelegate d) { WaitCallback w = delegate(object o) { try { d(); } catch (TestException ex) { Console.WriteLine("Exception caught"); Console.WriteLine("Exception message {0}", ex.Message); } }; ThreadPool.QueueUserWorkItem(w, new object()); }

    hank voight
    Tuesday, April 21, 2009 5:36 PM
  • You'll have to put throw; in the catch handler of Run1() if you want to catch it again in Run2().  Or remove the catch clause in Run1().
    Hans Passant.
    Tuesday, April 21, 2009 5:48 PM
    Moderator
  • E-mail sometimes isn't the best...   here is the fixed code.  I want to catch the exception....  I have been experimenting with 2 versions of calling the delegate, hence the 2 forms of the same method.  Run1 and Run2. 

    When Run1 is called the debugger stops on the throw, and says undhandled exception in user code.  When Run2 is called
    the debugger doesn't stop on the throw.

    In both cases the catch block catches the exception.  My question is why doesn't the debugger stop on the throw, for both method Run1 and Run2?

    using System;
    using System.Threading;
    
    namespace ConsoleApplication2
    {
    
        class Program
        {
            delegate void TestDelegate();
    
            static void Main(string[] args)
            {
                Console.WriteLine("Main Thread id = {0}", Thread.CurrentThread.ManagedThreadId);
    
                TestDelegate e = delegate()
                {
                    Console.WriteLine("Thread id = {0}", Thread.CurrentThread.ManagedThreadId);
                    throw new Exception("My exception");
                };
                
                Run1(e);
                Run2(e);
                
                string a = Console.ReadLine();
            }
    
            private static void Run1(TestDelegate d)
            {
                AsyncCallback ac = delegate(IAsyncResult ar)
                {
                    try
                    {
                        d.EndInvoke(ar);
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Exception caught");
                        Console.WriteLine("Exception message {0}", ex.Message);
                    }
                };
    
                WaitCallback w = delegate(object o)
                {
    
                    d.BeginInvoke(ac, d);
    
                };
    
                ThreadPool.QueueUserWorkItem(w, new object());
            }
    
            private static void Run2(TestDelegate d)
            {
                WaitCallback w = delegate(object o)
                {
                    try
                    {
                        d();
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Exception caught");
                        Console.WriteLine("Exception message {0}", ex.Message);
                    }
                };
    
                ThreadPool.QueueUserWorkItem(w, new object());
            }
        }
    }
    



     


    hank voight
    Tuesday, April 21, 2009 5:59 PM
  • I dunno, why does your debugger stop at all?  Go into Debug + Exceptions and check the Thrown flag on CLR exceptions.  When I turn that on, I get 3 breaks.  Twice on the throw new Exception("My exception") statement and once on d.EndInvoke() call.  The latter one is normal, EndInvoke() marshals the exception from the threadpool thread started by d.BeginInvoke() to the threadpool thread started by QUWI and re-throws it.  There's no marshalling needed in Run2(), the thread that raised the exception is also the one that catches it.

    Of course, you should never write code like this.  There's no point in having one thread start another and then wait for the result.
    Hans Passant.
    • Marked as answer by hank voight Tuesday, April 21, 2009 6:54 PM
    Tuesday, April 21, 2009 6:26 PM
    Moderator
  • Hmm,

    Checking on the Thrown Flag gives me the same results as you.  I don't know why there is a difference with the out of the box settings... 

    Thanks
    hank voight
    Tuesday, April 21, 2009 6:54 PM