11

Not long after upgrading to VS2010, my application won't shut down cleanly. If I close the app and then hit pause in the IDE, I see this:

alt text

The problem is, there's no context. The call stack just says [External code], which isn't too helpful.

Here's what I've done so far to try to narrow down the problem:

  • deleted all extraneous plugins to minimize the number of worker threads launched
  • set breakpoints in my code anywhere I create worker threads (and delegates + BeginInvoke, since I think they are labeled "Worker Thread" in the debugger anyway). None were hit.
  • set IsBackground = true for all threads

While I could do the next brute force step, which is to roll my code back to a point where this didn't happen and then look over all of the change logs, this isn't terribly efficient. Can anyone recommend a better way to figure this out, given the notable lack of information presented by the debugger?

The only other things I can think of include:

  • read up on WinDbg and try to use it to stop anytime a thread is started. At least, I thought that was possible... :)
  • comment out huge blocks of code until the app closes properly, then start uncommenting until it doesn't.

UPDATE

Perhaps this information will be of use. I decided to use WinDbg and attach to my application. I then closed it, and switched to thread 0 and dumped the stack contents. Here's what I have:

ThreadCount:      6
UnstartedThread:  0
BackgroundThread: 1
PendingThread:    0
DeadThread:       4
Hosted Runtime:   no
                                   PreEmptive   GC Alloc                Lock
       ID  OSID ThreadOBJ    State GC           Context       Domain   Count APT Exception
   0    1  1c70 005a65c8      6020 Enabled  02dac6e0:02dad7f8 005a03c0     0 STA
   2    2  1b20 005b1980      b220 Enabled  00000000:00000000 005a03c0     0 MTA (Finalizer)
XXXX    3       08504048     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    4       08504540     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    5       08516a90     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
XXXX    6       08517260     19820 Enabled  00000000:00000000 005a03c0     0 Ukn
0:008> ~0s
eax=c0674960 ebx=00000000 ecx=00000000 edx=00000000 esi=0040f320 edi=005a65c8
eip=76c37e47 esp=0040f23c ebp=0040f258 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
USER32!NtUserGetMessage+0x15:
76c37e47 83c404          add     esp,4
0:000> !clrstack
OS Thread Id: 0x1c70 (0)
Child SP IP       Call Site
0040f274 76c37e47 [InlinedCallFrame: 0040f274] 
0040f270 6baa8976 DomainBoundILStubClass.IL_STUB_PInvoke(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\WindowsBase\d17606e813f01376bd0def23726ecc62\WindowsBase.ni.dll

0040f274 6ba924c5 [InlinedCallFrame: 0040f274] MS.Win32.UnsafeNativeMethods.IntGetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2c4 6ba924c5 MS.Win32.UnsafeNativeMethods.GetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2dc 6ba8e5f8 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0040f318 6ba8d579 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0040f368 6ba8d2a1 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0040f374 6ba7fba0 System.Windows.Threading.Dispatcher.Run()
0040f380 62e6ccbb System.Windows.Application.RunDispatcher(System.Object)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\PresentationFramewo#\7f91eecda3ff7ce478146b6458580c98\PresentationFramework.ni.dll

0040f38c 62e6c8ff System.Windows.Application.RunInternal(System.Windows.Window)
0040f3b0 62e6c682 System.Windows.Application.Run(System.Windows.Window)
0040f3c0 62e6c30b System.Windows.Application.Run()
0040f3cc 001f00bc MyApplication.App.Main() [C:\code\trunk\MyApplication\obj\Debug\GeneratedInternalTypeHelper.g.cs @ 24]
0040f608 66c421db [GCFrame: 0040f608]

EDIT -- not sure if this helps, but the main thread's call stack looks like this:

    [Managed to Native Transition]  
>   WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x15 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x48 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame}) + 0x85 bytes 
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes  
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes  
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes 
    PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes 
    PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes 

I did a search on it and found some posts related to WPF GUIs hanging, and maybe that'll give me some more clues.

Dave
  • 14,618
  • 13
  • 91
  • 145
  • 1
    What is your main thread doing? – Tim Lloyd Dec 21 '10 at 01:16
  • Just sitting there waiting there for someone to click a button. – Dave Dec 21 '10 at 01:21
  • 1
    @Dave I mean what is it doing when you have "closed" the application? Is it executing some close event handler and waiting for something that will never happen? – Tim Lloyd Dec 21 '10 at 01:28
  • that's what I can't figure out. I actually misspoke earlier. When I try to pull up the call stack for the main thread, I get `[External code]`. When I do it for the worker thread, the call stack is completely empty. – Dave Dec 21 '10 at 04:46
  • @Dave And what dump do you get when you switch to thread 1 i.e. your main thread? – Tim Lloyd Dec 21 '10 at 12:08
  • @chibacity: took me a while to get back to this. There isn't a thread 1 (see above). It looks like 0 must be the main thread... – Dave Dec 22 '10 at 04:00
  • @Dave I would concentrate on making a small application to try and reproduce this. In particular I would pay attention to the code surrounding how the main window is displayed and how the user interacts with the application to shut it down, and the code that hooks up to this i.e. event handlers, etc. Producing a skeleton of startup and shutdown code that simulates your application would be a good start. – Tim Lloyd Dec 23 '10 at 18:45
  • @Dave Your main thread stack trace indicates that your main thread is still running a message loop and is idle waiting for a message i.e. GetMessageW. In reality closing the window should result in a WM_QUIT message being received, the message loop reading this, then exiting and the main thread unwinding i.e. normal application exit. For some reason it has not received WM_QUIT. – Tim Lloyd Dec 23 '10 at 18:56
  • @Dave Or it has not received a WM_CLOSE message for the main window. – Tim Lloyd Dec 23 '10 at 19:08
  • @chibacity Ok, I'll look into it. The app is pretty large but I'll try to narrow it down. I didn't see this problem until after I converted everything to .NET 4.0 for VS2010... could be related to something I did during the conversion. – Dave Dec 23 '10 at 20:40
  • @Dave Try out the WM_CLOSE program in my answer update first - at least we'll know then if the main thread is really hanging. – Tim Lloyd Dec 23 '10 at 20:51
  • @chibacity not sure if I can use Winspector on WPF apps, but I created a handler for my main GUI's Closed event, and it does get handled. I assume this implies that the WM_CLOSE message was received. – Dave Dec 23 '10 at 21:04
  • @Dave Please try out the sample application I have supplied - this will bottom out whether your main thread has hung or whether it is simply idle waiting for messages. – Tim Lloyd Dec 23 '10 at 21:09

5 Answers5

5

Add the following handler to every window your application creates in a separate thread:

win.Closed += (o, e) => win.Dispatcher.InvokeShutdown();

If the main thread hangs, call win.Dispatcher.InvokeShutdown() in MainWindow.Closed - this will automatically close all other windows created in the Main thread.

Without this handler, my application with the following code hanged on exit too:

void Worker() {
    var win = new Window();
    // win.Closed += onWindowClose ?? ((o, e) => editor.Dispatcher.InvokeShutdown());
    editor.Show();
    System.Windows.Threading.Dispatcher.Run();
}
Mykola Bohdiuk
  • 1,307
  • 12
  • 16
  • thanks for the suggestion, I'll add this as a preventative measure, but my app only has the main window. But you never know... I'll let you know how it goes. :) – Dave Dec 28 '10 at 14:42
  • +1, This fixed a related annoying problem I had. A third-party component was hanging the application on shutdown (I think it must've been creating a hidden window of some sort) and calling this from the main window Closed event fixed it. – Dan Bryant Oct 04 '12 at 16:18
4

I finally figured out this problem. I had a control that was Imported by MEF, but not actually ever called (yet). I think MEF instantiated it even though it wasn't referenced anywhere (I was under the assumption that creation didn't occur until the resource was requested, but apparently I was wrong). I fixed the issue by using Lazy<> instantiation, and now it works. This one really threw me, but thanks everyone for the help. I learned a lot trying to debug this problem.

Dave
  • 14,618
  • 13
  • 91
  • 145
3

The worker thread Id you are seeing is 0. This is a framework thread and is expected - it is not a thread that the program has "spawned". If you attach to any .Net process you will see this. I'm not sure which framework thread it is - definitely not the finalizer thread as this is never thread 0. Perhaps a JIT thread?

What is more interesting then is your main thread, as this appears to be hanging. I would concentrate on debugging your main thread to bottom this out. Has it deadlocked itself from a window closed event handler waiting for something that will never happen for example?

Update

After reading the stack trace for the main thread added to the question, it would be interesting to run a test to bottom out whether the main thread is stalled or whether it is simply idle waiting for a message (and that it is waiting for a WM_CLOSE that it never received or was never sent).

The code below can be used to manually send a WM_CLOSE message to your application. Simply wait for your program to hang when you have shut it down, and then run the code. Replace the process name with your own.

Update 2

Ok, it looks like the main thread is well and truly hung as it is not processing WM_CLOSE or WM_QUIT messages.

Please try and make the smallest application that can reproduce the issue and post the code.

Example WM_CLOSE\WM_QUIT App

internal class Program
{
    private const int WM_QUIT = 0x0012;
    private const int WM_CLOSE = 0x0010;

    [DllImport("user32.dll")]
    private static extern bool PostMessage(int hhwnd, uint msg, IntPtr wParam, IntPtr lParam);

    private static void Main()
    {
        Process p = GetProcess("Your process name - no '.exe' required");

        CloseMainWindow(p);
    }

    private static Process GetProcess(string name)
    {
        List<Process> processes = Process.GetProcessesByName(name).ToList();

        if (processes.Count != 1)
        {
            throw new Exception(
              "Expected 1 process with name '" + name +
              "' but found " + processes.Count + ".");
        }

        return processes[0];
    }

    private static void CloseMainWindow(Process p)
    {
        PostMessage(p, WM_CLOSE, "close");
    }

    private static void QuitApplication(Process p)
    {
        PostMessage(p, WM_QUIT, "quit");
    }

    private static void PostMessage(Process p, uint message, string name)
    {
        Console.WriteLine("Posting {0} message to '{1}'...", name, p.ProcessName);

        bool succeeded = PostMessage(p.MainWindowHandle.ToInt32(), message, IntPtr.Zero, IntPtr.Zero);

        Console.WriteLine("Posted {0} message to '{1}' (succeeded:{2}).", name, p.ProcessName, succeeded);
    }
} 
Tim Lloyd
  • 37,954
  • 10
  • 100
  • 130
  • @chibacity: I see what you are saying -- the Managed ID is 0, and that's what Thread.CurrentThread.GetHashCode() returns, isn't it? Hmm... I will look into what you have suggested, thank you. – Dave Dec 21 '10 at 05:05
  • @Dave The managed thread id is something that is managed by the framework and can be retrieved via "Thread.CurrentThread.ManagedThreadId". It is distinct from the native id which is managed by the OS. The thread with a managed id of 0 is a framework thread, not a thread that you have spawned or are using directly. This is the first thread that is managed by the framework, hence it has an id of 0. It comes before the main thread which has an id of 1, and before the finalizer thread which is 2. – Tim Lloyd Dec 21 '10 at 12:03
  • @chibacity Does it make sense that managed thread ID 0 comes and goes? Just to test things, I paused execution of the application and checked the threads that were running. I would open up various dialogs, pause, check threads, unpause. Sometimes managed thread ID 0 is there, sometimes it disappears, and sometimes it comes back. Does that mean anything? I'm trying to find resources online that will explain the significance of thread 0. – Dave Dec 23 '10 at 17:08
  • @Dave I do not know the gory details of how framework threads behave in detail. There is also scant information about this on-line - it is very much internal, so not widely published\documented. I still think you have a problem with your main thread though, and you are barking up the wrong tree looking at thread 0. I can't really help out without the source code! Can you not make a small reproducible sample? If you can't this is even stronger evidence that it is your main thread i.e. program specific. – Tim Lloyd Dec 23 '10 at 18:37
  • @chibacity thanks -- sorry, I got mixed up and was wondering where your answer update was. :) silly me. I will take a look at this. – Dave Dec 23 '10 at 21:13
  • @chibacity I get a PInvokeStackImbalance -- *A call to PInvoke function 'HungGUITest!HungGUITest.Program::PostMessage' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.* – Dave Dec 23 '10 at 21:18
  • @Dave Oops my bad, the signature for PostMessage was from memory. I have added two missing (but unused in this case) parameters to the signature. I missed it as you only get the unbalanced error when you run it with the debugger attached! Please use updated code. :) – Tim Lloyd Dec 23 '10 at 21:24
  • @chibacity no problem, I should have looked into it further, but I don't have much experience with PInvoke. I ran your application and it posted the message, but my app is still running. – Dave Dec 23 '10 at 21:55
  • @Dave Try running the application, but calling QuitApplication, instead of CloseMainWindow. – Tim Lloyd Dec 23 '10 at 22:06
  • @Dave Please make sure that your application does not have the debugger attached and you have not suspended its threads. – Tim Lloyd Dec 23 '10 at 23:15
  • @chibacity ok, I ran outside of the debugger and had Task Manager open so I could check on the process. I ran your app using CloseMainWindow and QuitApplication, and it's still running. – Dave Dec 23 '10 at 23:25
  • @Dave The main thread is definitely hanging then as it has not processed either message. Try sending either message to another application, you'll see it exit e.g notepad. – Tim Lloyd Dec 23 '10 at 23:30
  • @chibacity thanks so much for your help. I guess I'll have to resort to brute force debugging. – Dave Dec 23 '10 at 23:34
  • @Dave perhaps you have a call to Invoke or BeginInvoke that is hanging the main thread. One way of bottoming that out would be to write an IDispatcher proxy and use the proxy for all your dispatcher calls. Just forward the calls to the application dispatcher. Once you have a proxy in place you can wrap your Actions and add before\after logging. You should be able to see where your hang is as there will be a before but no after log entry. Consider using a counter and conditional breakpoint to nail it once you know what the pattern is. – Tim Lloyd Dec 24 '10 at 00:01
  • @chibacity great suggestion, I would have never thought of that. Thanks! I'll start working on it. – Dave Dec 24 '10 at 01:18
  • @chibacity I ended up taking the simpler route and set breakpoints on all calls to BeginInvoke. None of them got called in the mode of failure I'm trying to debug. – Dave Dec 28 '10 at 13:50
  • @Dave I would concentrate on making the smallest application that can reproduce the issue then. – Tim Lloyd Dec 28 '10 at 14:53
  • @chibacity Yes -- I'm going to try the brute force approach now -- roll back my code until the problem goes away, and then figure out what changes I made to the project that made it break. With any luck, such a rollback point exists, but it's also possible that when I converted the project to VS2010 the issue was already there. – Dave Dec 28 '10 at 15:16
  • 1
    @chibacity - see my answer here -- I figured out the problem. I guess I misunderstood how MEF instantiates objects. I'm going to award you the bounty, but have to wait 20 hours. Thanks a bunch for helping me learn some new things with .NET in the process. – Dave Dec 28 '10 at 16:59
  • @Dave Glad to see you got to the bottom of it - happy to have helped. Cheers for the bounty. :) – Tim Lloyd Dec 29 '10 at 13:49
  • @chibacity no problem. The funny thing is that I've got the same problem in a branch, and when I patched it, the problem didn't go away there. But since it's working in the trunk, that's the main thing at the moment. :) – Dave Dec 29 '10 at 15:21
1

If you are creating the worker threads (rather than pool threads), set their Name to something descriptive on creation.

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
  • If you're really in a jam you can rename pool threads using reflection. If you're desperate of [course](http://stackoverflow.com/questions/3353349/thread-renaming/3353400#3353400)... – Tim Lloyd Dec 21 '10 at 01:19
  • that's a good suggestion for the future, but I literally set breakpoints on *every* place I created a thread (so if I missed it there, I'd miss the Name as well :) ). Is there another place I can set a breakpoint when any thread is created, like an IDE setting? – Dave Dec 21 '10 at 01:21
  • 1
    @Dave when you use constructs such as BeginInvoke you are not actually creating a thread. A workitem is queued to the threadpoool and an existing thread picks it up. – Tim Lloyd Dec 21 '10 at 01:30
  • ok, thanks. I knew BeginInvoke uses the ThreadPool, but I thought they still showed up in the Threads window as `WorkerThread`. I guess not? I'll have to take a look... I know they're in there somewhere because I have seen them when single stepping through my code. – Dave Dec 21 '10 at 04:47
  • @Mitch thanks for the suggestion. While it doesn't solve my problem, I really like looking in the Threads window and now seeing something more meaningful than just "Worker Thread". :) Much better! – Dave Dec 21 '10 at 05:32
  • @Dave Yes, threadpool threads will appear in the threads window. I was referring to your question of "Is there another place I can set a breakpoint when any thread is created". This would not catch use of threadpool threads as you are not actually creating them - that's all. – Tim Lloyd Dec 21 '10 at 11:40
  • @chibacity Ah, I understand what you were saying. Thanks for the clarification. Will look into your other comment shortly. – Dave Dec 21 '10 at 14:34
1

Add logging to all your code to log the method and thread ID. You can use a regex replace to put it at the start of every single method. Then run the app and shut down. See what log messages have the same thread id as the worker thread that doesn't shut down.

Samuel Neff
  • 73,278
  • 17
  • 138
  • 182
  • The worker thread ID is 0 - it's a framework thread, although I'm not sure what its role is. I reckon it's actually a problem with the main thread. – Tim Lloyd Dec 21 '10 at 02:42
  • thanks. That's the best advice so far, I think. I already log all of this information with log4net, and it hadn't even occurred to me to follow the thread behavior back through the logs to get context that way! nice. :) – Dave Dec 21 '10 at 04:49
  • Interesting, the logs don't show 3256 anywhere, but 6524 was there. The application execution looks completely normal at this point, and functions fine until I shut down. I'll try changing the log4net configuration to log all entry points and we'll see if anything turns up. – Dave Dec 21 '10 at 05:31
  • 2
    The managed thread Id is 0, not the native id. This is the first thread that is managed by the framework, hence it has an id of 0. It comes before the main thread which has an id of 1, and before the finalizer thread which is 2. The managed thread id is managed by the framework, and is distinct from the native id which you refer to which is managed by the OS. As it is a dedicated framework thread, it has not been "spawned" by the program as such. It was started before the program (i.e. before Main) and is used by the framework. All .Net programs have this thread. – Tim Lloyd Dec 21 '10 at 12:06