views:

509

answers:

2

I'm designing a client-server chat application (in fact I'm not, but let's pretend I am:)), and I'm a bit puzzled by some race conditions I've experienced.

Let's say I've got the following code:

public interface IServer
{
  [OperationContract(IsOneWay = false)]
  [FaultContract(typeof(ChatException))]
  void BroadcastMessage(string msg);
}

public class Server : IServer 
{
  void BroadcastMessage(string msg) // I'm not mentionning the try/catch/throw FaultException here for readability purposes
  {
    foreach (IClientCallback c in callbacks){
      c.ReceiveMessage(msg);
    }

  }
}

public interface IClientCallback
{
  [OperationContract(IsOneWay = true)]
  void ReceiveMessage(string s);
}

And here is an excerpt of the binding configuration :

<endpoint address="" 
binding="netTcpBinding" 
bindingConfiguration="DuplexBinding" 
contract="IServer" />

 <binding name="DuplexBinding" sendTimeout="00:01:00">
   <reliableSession ordered="true" inactivityTimeout="00:05:00" enabled="true"/>
   <security mode="None">
     <transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
     <message clientCredentialType="Windows" />
   </security>
 </binding>

This is of course some sort of pseudo c#, I've removed a lot of non-relevant code for clarity sake.

Now to the point : This code won't work. When I call BroadcastMessage, the method never returns, and I eventually get a timeout on the client side. If I debug on the server side, everything seems fine (I return from the BroadcastMessage method exactly as one would expect, and I'm not blocking on any ReceiveMessage one way calls)

Here are two ways to fix this code :

  1. remove the FaultContract and declare the BroadcastMessage method as oneway=true
  2. Broadcast the message to everybody BUT the initial sender

My first guess was that the client side was waiting for the server to return, and thus wasn't available for handling the incoming ReceiveMessage call from the server, thus blocking the server, BUT ReceiveMessage is declared as oneway, and debugging the server shows that it doesn't block on any call to ReceiveMessage

Now, my questions :

  • What's going on?

  • Are there other ways to fix this? (maybe by tuning the binding configuration ?)

  • Let's say I choose fix 2 (ie don't broadcast back to the sender), what happens if the server calls my ReceiveMessage callback (because someone else sent me a message) while I'm waiting for my own BroadcastMessage call to finish?

  • I've read that OneWay calls are not totally oneway, and that the server still waits for an HTTP response from the other side. Any details on this? specifically, is the client able to respond suche http responses when it's blocked in a distant call?

Edit: Console .net 3.5 on the server side, Winforms .net 3.5 on the client side

+2  A: 

It sounds like a deadlock, perhaps due to the sync-context. What is the client? Winform? WPF? WCF respects sync-context, which means "switch to the UI thread" for winforms and WPF. If you are making the blocking request on the UI thread, then game over.

Try performing your WCF request on a background thread, so that the UI thread is available to service the incoming request; this could be as simple as using ThreadPool, or maybe BackgroundWorker.

Marc Gravell
Indeed, I'm making my call from the UI Thread.I'll try inovking it on another one. Still, I don't understand why the deadlock happens. The server shouldn't wait for the client to respond, should it? Could you be more specific on what happens behind the scene here? many thanks!
Brann
You have one-way=false... so it *should* wait.
Marc Gravell
Invoking from another thread did fix the problem !:)
Brann
I was speaking about the ReceiveMessage callback (which has oneway=true). It seems that when the UI thread on the client is busy waiting for the return from a non-oneway call, if it receives another call, things are broken. In other words, one should never make a non-oneway call from the UI thread ?
Brann
Normally that would be fine. The problem is that you are also the server. It is the server that does the sync-context switch. A pure client isn't affected by this so much.
Marc Gravell
What do you mean by "I'm also the server?" ... there's always a server, isn't it? Do you have a link explaining in details what's happening behind the scenes?
Brann
+1  A: 

try adding this attribute to your concrete implementation of your service:

[System.ServiceModel.ServiceBehavior(UseSynchronizationContext=false)]
public class Server : IServer {}

Secondly, try turning on WCF tracing to see if you have something happening in the bowels of your WCF implementation which is giving your grief. I've had some really weird errors which only made sense when I went into tracing and found out the ACTUAL error message that was occuring.

Tracing a WCF Service

Spence
Unfortunately, adding this attribute didn't fix the problem. Still, it was worth trying! thanks:)
Brann