Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to figure out who owns a worker thread that is still running when my app exits?

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.

like image 971
Dave Avatar asked Dec 21 '10 01:12

Dave


People also ask

How do I see running threads in Visual Studio?

To display the Threads window in break mode or run mode While Visual Studio is in debug mode, select the Debug menu, point to Windows, and then select Threads.

What are worker threads?

Worker thread is a continuous parallel thread that runs and accepts messages until the time it is explicitly closed or terminated. Messages to a worker thread can be sent from the parent thread or its child worker threads. Through out this document, parent thread is referred as thread where a worker thread is spawned.

How many threads are there in Android?

Android has four basic types of threads. You'll see other documentation talk about even more, but we're going to focus on Thread , Handler , AsyncTask , and something called HandlerThread .


4 Answers

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();
}
like image 181
Mykola Bogdiuk Avatar answered Sep 28 '22 03:09

Mykola Bogdiuk


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);
    }
} 
like image 28
Tim Lloyd Avatar answered Sep 28 '22 03:09

Tim Lloyd


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.

like image 42
Dave Avatar answered Sep 28 '22 01:09

Dave


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

like image 29
Mitch Wheat Avatar answered Sep 28 '22 02:09

Mitch Wheat