views:

41

answers:

3

We're having a serious issue at work. We've discovered that after the station where the client was running is locked/unlocked the client is blocked. No repaint. So the UI thread is blocked with something. Looking at the callstack of the UI thread (thread 0) using windbg we see that a UserPreferenceChanged event gets raised. It is marshalled through a WindowsFormsSynchronizationContext using it's controlToSend field to the UI. It gets blocked by a call to the marshalling control. The method called is MarshaledInvoke it builds a

ThreadMethodEntry entry = new ThreadMethodEntry(caller, method, args, synchronous, executionContext);

This entry is supposed to do the magic. The call is a synchronous call and because of that (still in the MarshaledInvoke of the Control class) the wait call is reached:

if (!entry.IsCompleted)
{
    this.WaitForWaitHandle(entry.AsyncWaitHandle);
}

The last thing that i can see on the stack is the WaitOne called on the previously mentioned AsyncWaitHandle. This is very annoying because having just the callstack of the runtime and not one of our methods being invoked we cannot really point to a bug in our code. I might be wrong, but I'm guessing that the marshaling control is not "marshaling" to the ui thread. But another one...i don't really know which one because the other threads are being used by us and are blocked...maybe this is the issue. But none of the other threads are running a message loop. This is very annoying. We had some issues in the past with marshaling controls to the right ui thread. That is because the first form that is constructed is a splash form. Which is not the main form. We used to use the main form to marshal call to the ui thread. But from time to time some calls would go to a non ui thread and some grids would broke with a big red X on them. I fixed this by creating a specific class:

public class WindowsFormsSynchronizer
{
    private static readonly WindowsFormsSynchronizationContext = new WindowsFormsSynchronizationContext();
//Methods are following that would build the same interface of the synchronization context.
}

This class gets build as one of the first objects in the first form being constructed.

We've noticed some other strange thing. Looking at the heap there are 7 WindowsFormsSynchronizationContext objects. 6 of these have the same instance of controlToSend, and the other one has some different instance of controlToSend. This last one is the one that should marshal the calls to the UI.

I don't have any other idea...maybe some of you guys had this same issue?

A: 

While this may not specifically address your issue, you can see the call stacks of other threads (assuming you're debugging in Visual Studio). There is a debug window called "Threads" (in the default menu configuation, Debug->Debug Windows->Threads). With this visible, you can double-click on any thread to switch to it. This will load that thread's call stack and should hopefully give you a little more information about what's going on.

Adam Robinson
+1  A: 

Yes, events generated by the SystemEvents class (SessionSwitch here) are apt to cause deadlock when a program is violating Windows threading rules. It isn't clear to me exactly how that came about in your case but it sounds to me that you've been battling threading issues for a while already.

.NET 2.0 has built-in diagnostics for this. Be sure to leverage this, it only works in the debugger. Make sure you didn't set Control.CheckForIllegalCrossThreadCalls to false.

Hans Passant
Didn't know of Control.CheckForIllegalCrossThreadCalls. I'll look into it.
Silviu
A: 

Yesterday we did slay the monster. My original hunch was that the faulty behavior was given by the Application starting form was the splash form which was later switched for the real main form. The issue was that 2-4 months ago the splash screen was made to slowly dissapear. This was done using a timer. But the splash screen was used for logging in and building up some caches and loading some user preferences. Some of this actions involved the construction of some controls. To make the splash screen vanish the transparency would get changed from time to time to lower values. This was handled on the UI thread and the other time consuming calls were sent to the ThreadPool. This involved the creation of controls on other threads. For some reason some of these controls got used for marshaling back to the UI thread. Anyhow fixed it by remarshaling these call to the UI thread.

Silviu