Finding Illegal Cross Thread Calls

We recently had some customers reporting issues with our software, the reports were of the UI ‘locking up’, and never coming back.
This plays out as a weird scenario, where the UI would become totally unresponsive to any user interaction, however from Window’s (task manager’s) point of view, all seems peachy and the application is reported as ‘responsive’.

The cause

The problem stems from creating UI elements in a background worker (non-MainIUThread); then the application seems to hang usually when the computer comes out of sleep mode, unlocks, user changes resolution or desktop background, or another similar event occurs. The hang happens in the firing of an event handler, called from “SystemEvents.OnUserPreferenceChanged”
It’s been described in good detail here: http://ikriv.com/en/prog/info/dotnet/MysteriousHang.html

 

Finding controls that are being created on the wrong thread

The way I did this was to set a break point to trigger under the condition where UI is being created in the wrong thread.

Step 1. Set a breakpoint deep in the bowels of the BCL.
What we want to do is cause our application to break when a WindowsFormsSynchronizationContext gets assigned to the wrong thread.

WindowsFormsSynchronizationContext

We see in the constructor for WindowsFormsSynchronizationContext that there’s a call to “Application.ThreadContext.FromCurrent()”. We need to add a breakpoint to that method call. In Visual Studio, go to the “Debug” > “New Breakpoint” > “Break at Function…” menu. In the “Function” area type the full path to the function: System.Windows.Forms.Application.ThreadContext.FromCurrent()

Break at Function “Application.ThreadContext.FromCurrent()”

When you hit “OK” you’ll get a warning about IntelliSense not finding the specified location. Hit “Yes” to set the breakpoint anyway.

Step 2. Add a filter to this breakpoint so that it only breaks when the current threads’ name isn’t “MainGUI” (or “Main Thread”).
Go to your breakpoint window (Debug -> Windows -> Break Points); and find the breakpoint that we just added. Right click on that break point and select “Filter” from the context menu.

breakpoint filter

Set the correct filter so that VS doesn’t break when things are the way they should be… i.e. !(ThreadName = “MainGUI” || ThreadName = “Main Thread”)

breakpoint filter conditions

Hit “OK”

Step 3. Run Your Software/UI
And the first time Visual Studio comes across a scenario where WindowsFormsSynchronizationContext is created and it’s not on the UI thread, BAM. There’s your problem, and there’s your stack trace allowing you to diagnose the bad code.

Invoke Required

The other thing I played around with was logging to a Trace when/if things were not being invoked correctly by .NET itself! This seems to be a documented issue (race condition) in .NET 2.0 but I haven’t seen it occur in .NET 3.5. However since .NET 3.5 is based on 2.0 I figure there’s a chance it still could happen so why not leave the debug code to point out if/when it occurs…
I created a method called “TraceWasInvokeRequired” in the Main.UI.Context class and ensured that we use this around anywhere where we are checking if InvokeRequired. The method will just Trace out who’s calling the invoke, if invoke was required and what thread it’s running in. It will give a bigger warning if invoke “wasn’t reqruied” and we’re not in “Main GUI”. There’s how it’s used…
EXAMPLE:
=========Change from =========
if (InvokeRequired)
BeginInvoke(new MethodInvoker(UpdateDashboardTitle));
else
UpdateDashboardTitle();

=========Change to =========

if (Context.TraceWasInvokeRequired((this.InvokeRequired), Thread.CurrentThread.Name, (GetType().FullName)))
BeginInvoke(new MethodInvoker(UpdateDashboardTitle));
else
UpdateDashboardTitle();
============================

Also;

you need to ensure you don’t have ‘Just My Code’ debugging turned on in VS.

Set 'Just My Code’  debugging

Leave a Reply

Your email address will not be published. Required fields are marked *