Aaron Murrell

Aaron's blog - a few choice words.

8/14/2007 7:11 PM (UTC+05:30)
by aaron

There's not much that's more frustrating than taking the time early on in development to work out your unit tests only to discover later during integration that what worked beautifully in NUnit and VSTS tests doesn't actually work in the context of the larger system.  I don't think I need to spell out what's coming next.  Yup... I'm a victim.

First, the morals of the story:  Don't let WCF synchronization context bite you in the ass; and there's no substitute for a good book and subsequently the knowledge you can put in your head using the right book.

Ok, here's the scenario that you never want to find yourself in:

Ingredients:

  • One console app WCF host (substitute WAS host or Windows Service host if desired)
  • One windows forms app client
  • One WCF service with a callback

here's the layout:

 

image

Inside the Winforms client I get a reference to my service proxy and pass in an instance of a class that implements the service's callback contract (which happens to be the form itself) ... no big deal.  It looks something like this:

public partial class Form1 : Form, ICalculatorServiceCallback
{
    ICalculatorService proxy;
    public Form1()
    {
        InitializeComponent();
        System.ServiceModel.Channels.Binding binding = 
            new NetTcpBinding(SecurityMode.Message, true);
        EndpointAddress address = 
            new EndpointAddress(@"net.tcp://localhost:4000/CalculatorService");


        InstanceContext context = new InstanceContext(this);
        DuplexChannelFactory<ICalculatorService> factory = 
            new DuplexChannelFactory<ICalculatorService>
            (context, binding, address);
        proxy = factory.CreateChannel();
    }       

    private void Form1_Load(object sender, EventArgs e)
    {

    }

Here is the implementation of the callback contract further on down in the form:

 

        #region ICalculatorServiceCallback Members

        public void AddInvoked()
        {
            System.Diagnostics.Debug.WriteLine("Yo!");
        }

        #endregion
 

Finally, a button clicked routine (also defined in the form) to actually use the proxy:

        private void button1_Click(object sender, EventArgs e)
        {       
            proxy.Add(23, 44);
        }
 

As for the service side, the host is a pretty typical console type host that again imperatively sets up a singleton instance of the CalculatorService which implements the ICalculatorServiceContract:

class Program
    {
        static void Main(string[] args)
        {
            ICalculatorService singletonCalculator = 
                new CalculatorService();
            
            ServiceHost calculatorHost = 
                new ServiceHost(singletonCalculator);

            NetTcpBinding binding = 
                new NetTcpBinding(SecurityMode.Message, true);
            Uri address = 
                new Uri(@"net.tcp://localhost:4000/CalculatorService");

            calculatorHost.AddServiceEndpoint(
                typeof(ICalculatorService), binding, address);

            calculatorHost.Open();
            Console.WriteLine(calculatorHost.State);
            Console.WriteLine
                ("CalculatorService started, press any ENTER to close...");

            Console.ReadLine();
            calculatorHost.Close();
        }
    }

The service looks pretty much like you would expect.  The important part of the actual service is that it is decorated with a ServiceBehavior attribute indicating that the InstanceContextMode shoudl be set to single, thus making our service a singleton.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
public class CalculatorService : ICalculatorService
{

    #region ICalculatorService Members
    private Dictionary<string, float> memory = 
        new Dictionary<string, float>();

    public void Add(params float[] nums)
    {            
        Console.WriteLine("The Add Method was invoked by {0}", 
            System.Threading.Thread.CurrentPrincipal.Identity.Name);
        DoCallback(nums);
    }

    private void DoCallback(float[] nums)
    {
        ICalculatorServiceCallback callback = 
            OperationContext.Current.GetCallbackChannel
            <ICalculatorServiceCallback>();
        callback.AddInvoked();
    }

So far nothing interesting right?  In fact I don't even think there's even any adding logic in there.  Oops.  Ok, here is where things start to get a little wiley.  The symptom I was seeing was manifesting itself in the Winforms client.  The CalculatorService Add() method would get called appropriately in the button event, but then the Winforms app would unexpectedly freeze.  Eventually I'd see a timeout error from the client's proxy and then the callback would fire.  Weird.  Connectivity issues were hard to prove because the service call was being serviced, and the callback would eventually find its way to the client callback method.  The more I looked the more it became painfully obvious that I had a threading deadlock issue.  Keep in mind that this all worked in my 'integration' unit tests that did the hosting and the client calls in the same process.  Then I remembered reading about concurrency modes and it seemed to make sense that the default ConcurrencyMode setting on the service might be the problem.

In a nutshell, you can instruct WCF to dispatch simultaneous incoming calls to your service in several different ways with respect to threads.  This is done using the ConcurrencyMode attribute on your service.  The default mode is ConcurrencyMode.Single.

ConcurrencyMode.Single tells WCF to lock concurrent access to the service down to one thread at a time.  Incoming calls are queued up and dispatched in order.  Any call that waits in the queue longer than the service's timeout will throw a timeout error on the client. 

ConcurrencyMode.Multiple and ConcurrencyMode.Reentrant are also available. 

The advantage of using ConcurrencyMode.Single (and I'm sure this is why it was chosen as the default value) is that it is threadsafe by nature.  As a service developer, you don't have to worry about locking concurrent access to service state inside service logic because the entire service is locked down by WCF.  If you want to handle things manually you can choose ConcurrencyMode.Multiple.  ConcurrencyMode.Reentrant is a unique beast and we'll come to that in a moment. 

Now, if you plan to callback to your client(s) during the actual execution of a service operation, having the default mode of ConcurrentMode.Single just won't do.  WCF will attempt to dispatch the callback, find that the resources it is allowed to use are being occupied by the execution of the original service method and patiently wait - DEADLOCK.  This is exactly what was happening.  Solution?  Change the ConcurrencyMode to ConcurrencyMode.Reentrant.  This gives WCF permission to abandon the original thread lock on the service and fire the callback.  Much to my surprise, this didn't solve the problem with my windows forms app blocking.  In fact I didn't notice any difference at all.

It was only after spending a few more hours with Juval Lowy's excellent book : Programming WCF Services this weekend that I was able to isolate the problem to one that involved the client's use of SynchronizationContext.

SynchronizationContext is a new beast in .Net 2.0 and affords you the ability to affinitize thread execution.  What does that mean?  Well, one example of its use is where I want to guarantee that some method executes on the same thread that the UI is running on in my windows app.  By applying the use of a synchronization context I can make this guarantee, asking .Net to marshal the thread of execution onto the thread that the UI is running on. 

In WCF there are several mechanisms available to tweak the behavior of the SynchronizationContext for different aspects of your services, but the one that concerns us here is the one that affects the affinity of the thread that the client's callback thread is dispatched on.  By default callbacks are set up to affinitize to the SynchronizationContext of the thread that creates them.  In our case, the UI thread created our callback object and execution of our callback was bound to the UI thread.  This created a problem because we were also dispatching our original call to the Add method on the UI thread.  The callback couldn't be invoked because it was in a deadlock waiting on the original call to Add to complete.  This is why when the timeout exception was thrown, the callback finally was able to get through.

To change this behavior, you can set the UseSynchronizationContext parameter of the CallbackBehavior  attribute to false, indicating that the callback should not be affinitized to the thread that creates the callback.

The fixed portions of our failing code are below:

 

First change the ConcurrencyMode to Reentrant:

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, 
        ConcurrencyMode=ConcurrencyMode.Reentrant)]
    public class CalculatorService : ICalculatorService
    {

...

 

Second, change the CallbackBehavior of the object implementing our service's callback

[CallbackBehavior(UseSynchronizationContext=false)]
    public partial class Form1 : Form, ICalculatorServiceCallback
    {
        ICalculatorService proxy;
        public Form1()
        {
            InitializeComponent();

...

 

I certainly hope this helps save someone some time. 

On 8/23/2007, Kevin said:
I came across the same issue. Thanks a lot for sharing the solution.
On 1/3/2008, deyan said:
Thank you. This was very good and saved me hours right now!
Excellent.
On 3/31/2008, Simon said:
Hi, I am using WCF in a client-server architechture, my server has a UI and is configured as follows:

[ServiceBehavior(UseSynchronizationContext = true, InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Multiple)]

Meaning that all calls done from my clients are marshalled to the UI thread to be handled by the server. This makes sense in the context of my application, but I have one call (two-way) in particular that is quite lenghty and that is reentrant. I would therefore like my server to handle that specific method asynchronously.
Following the research you have done for your article, to you know if it is possible to configure my server to globaly use the UI thread to synchronize the calls (UseSynchronizationContext = true), but to override that property on a per-call basis to specify a few methods that should be handled asynchronously?

Thanks in advance.
On 7/11/2008, Ben said:
Thanks a lot! I was looking for this solution for 4 hours...
On 8/5/2008, lee said:
Very good. It resolve my err below

System.ServiceModel.CommunicationObjectAbortedException: The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.
On 8/23/2008, tom said:
thanks a lot! this was saving me hours!
On 4/23/2009, Tuan Jinn said:
Awesome,
Saved a lot of times. I came across another problem (which similar to Lee's one.
Service aborted...
Any clue why he solved that and me not?
On 5/22/2009, jirka said:
I had the same problem and spent couple of hours before finding your article. Thanks a lot.
On 5/27/2009, David said:
Thanks man....This problems cause me few days...Your article have makes my code works
On 6/11/2009, Andres Serrano Garcia said:
Whooooooou
Thanks men I spend a lot of time trying to use a singleton service with a callback, your post make me pass this timeout problem.
On 6/11/2009, Greg Christos said:
I know this should be straight forward, but I'm still getting a deadlock. Do you have a sample that can be downloaded. I'm missing something here.

Thanks Aaron
On 6/30/2009, Anand said:
Thanks for the helpful article. Saves someone's day any day!
On 7/9/2009, chris said:
amazing artice!
finally figured what the problem was.
thx a lot
cheers
On 7/21/2009, drzut said:
thanks! this saved me hours!
On 7/22/2009, ranjit said:
Great article. Just want to add that if soeone does nto want the concurrency mode to be reenterant, then instead the operation contract can be marked as IsOneWay = true on the Service Contract. This means that service will not try to return anything to the client immediately after we call the service menthod. After the execution of the method finished on the server. The client can be called back to return the values.
On 9/19/2009, Mike McLuckie said:
Thanks very much for the article!
On 9/30/2009, Pharaoh said:
Waaauuuu !!!! This issue took me nearly a whole day at work. I managed to make a workaround by invoking callback in separate threads on server side, but this was a solution I was looking for. Thanks man. :D
On 10/7/2009, HP said:
Great, thank you very much!
On 11/5/2009, Stewart said:
A gentleman and a scholar! thank you for saving me many hours of head scratching.
On 11/20/2009, Anton said:
Clear and concise article, solved my TimeException issues. Many thanks.
On 12/1/2009, venu said:
Good one..
On 1/10/2010, Evgeny said:
Thank! Very usefull post!
On 2/15/2010, ashutosh said:
You rock!! saved me hours. thanks for posting
On 3/31/2010, Darin said:
A big thanks for this post. I was running into the same problem. Knew it had to be a threading issue, but as with most things "framework" knowing the right I to dot is the entire battle!
On 4/8/2010, Alex said:
Thanks a ton for this article! I've been looking for this solution for hours:)
On 4/29/2010, Roman Golubin said:
Your article translated into Russian
Add a Comment
Name* 
Email
Home Page
Comment*
 
Url for details
Verification*
 
Captcha