Intrigue is curious thing, much as curiosity is intriguing. What drives it? Mostly just needing to know something for no other reason than wanting to know itself – curious indeed. 

So why would I really be bothered to know what WindowsFormsSyncronizationContext does under the hood? Well dear bleader (if someone who write blogs is a blogger, maybe someone who reads blogs is a bleader) I had my suspicions, but I just needed to find out.

SynchronizationContext and it’s derivatives was introduced in .NET 2.0 as simple .NET language construct to allow a non-GUI thread to call methods on a GUI control. Previously, code had to be written to perform the InvokeRequired, BeginInvoke, Invoke dance and as has been discovered, this has problems and  can have you chasing nasty bugs.

In https://davebrooks.wordpress.com/2007/02/12/begininvoke-the-land-of-confusion/ I discussed BeginInvoke for delegates and controls, some issues and how they worked under the hood.

As I said before, I was suspicious that WindowsFormsSynchronizationContext worked pretty much the same way. And Lo, with a bit of windows message spying on a simple test app, my suspicions were confirmed. WindowsFormsSynchronizationContext.Post() and .Send() end up using PostMessage with registered windows messages, presumably using BeginInvoke and Invoke respectively to push the control method call onto the main GUI thread.  However, it does make the code earlier to write and as we all know, less code means less bugs.

Now to find out what happens in the Freemasons. I’m just curious you understand.

Advertisements

Over the last couple of weeks, I’ve had to do a few recruiting interviews for Lab49: It’s been illuminating. Delegates are always a good things to chat about and usually, a good candidate will easily rattle off what Invoke and BeginInvoke are and what they do. Sometimes you even get an explanation about how BeginInvoke uses a thread allocated from the ThreadPool. So how does BeginInvoke for a form or controls work? “Oh it works the same doesn’t it? Ooooo, not too sure now you mention it”.

Despite Invoke and BeginInvoke having the same signatures for Controls and delegates, they work very differently.

Delegate Invoke
The method referred to by the delegate instance is called on the target object (if there is one), and the result is returned.

Delegate BeginInvoke
This uses a thread allocated from the ThreadPool to execute the target method. So although the thread creation time is generally fast, there are only 25 threads (by default) per CPU so don’t go hogging them for time consuming stuff.

Control/WinForm BeginInvoke
This doesn’t use a thread to execute the referenced delegate. Instead it uses good old PostMessage to put a registered message on the windows message queue. It’s then picked up by the applications message pump and the target will then be execute on the main GUI thread. Remember that PostMessage returns immediately the message has been put on the queue – that’s how it make the async call.

Control/WinForm Invoke
Those of you still listening, may assume Control Invoke therefore uses SendMessage to make the synchronous call. But no. A little investigation reveals that Invoke and BeginInvoke preserve the calling order, so it seems Invoke is doing the same as BeginInvoke except the call is synchronous. Under the hood, Invoke maybe does something like:

object Invoke(Delegate method)
{
  return EndInvoke(BeginInvoke(method, null));
}

This is confirmed by spying the windows message when Invoke and BeginInvoke are used. The mysterious  WindowsForms12_ThreadCallbackMessage registered message appears in message queue in both cases. I guess the motivation for this was being kind to the message pump. Messages will still be pumped while an Invoke is being called.

A word of caution when using Control Invoke/BeginInvoke. Because they both post messages to the message queue, you might expect everything to behave sweetly. However, if you repeatedly call the control delegates quickly, you’ll suddenly find your GUI will be unresponsive and won’t paint. This is because WM_PAINT and WM_TIMER are the amoeba of the windows message world and will always get pushed to the bottom of the message queue. If you have to deal with high data feed rates, use another mechanism to control the frequency they arrive, otherwise you’ll end up with a sticky GUI. 

Here’s an example of a safe control wrapper that uses control delegates. There’s a plethora of these type of classes, but this is my own twist on it. This approach uses BeginInvoke when called from the non-GUI thread. It results in the GUI being generally more responsive because it’s not having to wait for the the delegate to respond. I also use a technique I’ve called self-referencing delegates. When an invoke is required, the code creates a new delegate to itself and BeginInvokes it. When the same referenced method gets called in the main GUI thread, the base method is called.  

public class SafeTextBox : TextBox
{
  delegate void Set(string text);
  delegate string Get();
  override public string Text
  {
     set
     {
        if (InvokeRequired)
        {
           Set setDelegate = delegate(string text) {Text = text;};
           BeginInvoke(setDelegate, new object[] {value});
        }
        else
           base.Text = value;
     }
     get
     {
        if (InvokeRequired)
        {
           Get getDelegate = delegate() {return Text;};
           return (string)EndInvoke(BeginInvoke(getDelegate, null));
        }
        else
           return base.Text;
     }
  }
}

It’s interesting that despite C# and the CLR making our lives easier by providing simpler code constructs and managing memory for us, it’s still very easy to screw things up; especially when it comes to thread safety and race conditions. In the good (bad) old days of C++, delete, _beginthread, etc you had to write more code to develop your multithreaded apps and particularly when developing multithreaded GUIs you had to take care and understand more what was actually happening. If you ended up with a race condition, deadlock or message pump block, it was easier (relatively) to see, hear and smell where the problem was under the hood.

By providing events, delegates and simple threading constructs, C# makes writing multi-threaded GUIs really rather straightforward. But it’s exactly those simple constructs that means developers do more with them and consequently find more devious ways to make things go wrong in seemingly non-deterministic ways.

Take events and delegates. The often quoted, thread unsafe example is:

public class EventPublisher
{
   public event EventHandler theEvent;
   public void InvokeEvent()
   {
      // If delegate has no targets it will be null
      if (theEvent != null)
      {
         // So it seems OK to use, but a thread context
         // switch may happen here and remove all the
         // invocation targets.
         theEvent(this, EventArgs.Empty); // Maybe boom!
      } 
   }
}

Clearly, there is a window of opportunity for it to go wrong. And if there’s an open window …..

One counter measure is to make a copy of the delegate. Delegates are immutable so by copying it to a temporary variable you keep a copy of the original state of the delegate, irrespective of any thread context switches. Making any change to the state of the delegate creates a new one on the heap and updates the reference to which the delegate point.

Remember, EventHandler is a shorthand declaration of both a delegate variable and an event of the same type at the same time, so when I’m talking delegates it’s an EventHandler in the code snippets.

But there’s still an open window for a race condition …

The JIT compiler may decide to optimize out your copying for performance reasons, so you are back where you started. Yet another published solution, again uses copying, but this time the copy happens by passing the delegate (EventHandler) as a method parameter.

public class EventPublisher
{
   public event EventHandler theEvent;
   public void InvokeEvent()
   {
      InvokeEvent(theEvent);
   }
   private void InvokeEvent(EventHandler handler)
   {
      if (handler != null)
      handler(this,EventArgs.Empty);
   }
}

On the face of it this looks to close the race condition window of opportunity, but surprisingly it is still open. The JIT compiler’s optimizations may still beat you and decide to inline your method. A method will be a candidate for inlining when its code is ‘small’; just a few lines of code (less than 32 bytes of IL). Exact details are not easy to find, but David Notario has written a couple of articles on JIT Optimizations that are worth a read; JIT optimiations I and JIT optimizations II

The answer, to be safe, is to prevent inlining for the method using the [MethodImpl(MethodImplOptions.NoInlining)] attribute.

public class EventPublisher
{
   public event EventHandler theEvent;
   public void InvokeEvent()
   {
      InvokeEvent(theEvent);
   }

[MethodImpl(MethodImplOptions.NoInlining)]
   private void InvokeEvent(EventHandler handler)
   {
     if (handler != null)
        handler(this,EventArgs.Empty);
   }
}

Hopefully the window is now tightly closed.