views:

192

answers:

3

Hi I seem to have a blocking issue with WCF.

Say I have two users and each have created their own instance of a class exposed on a WCF host using net.tcp with endpoint something like this "net.tcp://localhost:32000/SymHost/". The class is PerSession context and concurrency is reentrant. The class exposes two methods Alive() which return a bool of true straight away and an AliveWait which I inserted which does a Thread.Sleep for 4 seconds before returning true (testing purposes).

Now client 1 calls AliveWait() during which time he is blocked which is fair enough but then if client 2 makes a call to Alive() on its own instance he has to wait until client 1's call is returned - this behaviour is not what I would have expected? I would have expected client 2 to carry on as if nothing has happened or is this to do with the fact that they both share the same endpoint?

Can anyone explain what is going on and how I can make sure that client 2 can call its own instance uninterrupted?

Any help much appreciated!

A: 

Show us the service class ! Just from your description, it's impossible to tell what's going on.

I would not expect client 2 to be blocked by client 1 - in a per-session scenario, the two clients should each get their own, totally independent service class instances. That's why we need to see what kind of code we're dealing with here.... normally, there should not be any problem with that kind of setup.

marc_s
That is exactly what I thought - I am posting the code as an answer to my question since I exceeeded their 500 chars limit!
Marcel
I found out what it was just now by going through each and every attribute WCF exposes and what I had forgotten was to put the UseSynchronizationContext to false in the ServiceBehaviour of the Test class. This defaults to true which synchronises all calls to the service to run on the same thread! So any subsequent calls from other clients were simply queued!
Marcel
A: 

That is exactly what I thought! So here is some of the source code (after bit of copy and pasting...):

-- contract definition

[ServiceContract(CallbackContract = typeof(IAliveCallback))]
public interface IAlive 
{
    [OperationContract]
    bool Validate();

    [OperationContract]
    string AliveWait(int i);    // test test
}

-- implementing the contract in my Alive class

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Single)]
public class Alive : SymHostBase, IAlive
{
    private readonly static string _ID = "Alive";
    private static int _MaxAliveWaitSeconds = 5;

    public bool Validate()
    {
        return true;
    }

    public string AliveWait(int i)
    {
        Thread.Sleep(i * 1000);
        return string.Format("I waited {0} seconds", i);
    }

...
...
}

-- then on the Host app it looks like this...

        string s = string.Format("net.tcp://localhost:{0}/Host/", port);
        Uri tcpAddr = new Uri(s);
        Uri[] baseAddress = { tcpAddr };

        int MaxBuffer = 64;     // 64 Mb
        int bufferSize = MaxBuffer * 1024 * 1024;   // 67108864

        NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.None, true);
        tcpBinding.MaxBufferPoolSize = bufferSize; // 64 Mb
        tcpBinding.MaxBufferSize = bufferSize;
        tcpBinding.MaxReceivedMessageSize = bufferSize;
        tcpBinding.TransferMode = TransferMode.Buffered;
        tcpBinding.ReaderQuotas.MaxArrayLength = bufferSize;
        tcpBinding.ReaderQuotas.MaxBytesPerRead = bufferSize;
        tcpBinding.ReaderQuotas.MaxStringContentLength = bufferSize;

        tcpBinding.MaxConnections = 100;
        //tcpBinding.ReceiveTimeout = new TimeSpan(20, 0, 0);
        tcpBinding.SendTimeout = new TimeSpan(0, 0, 5);     
        tcpBinding.ReliableSession.Enabled = true;
        tcpBinding.ReliableSession.InactivityTimeout = new TimeSpan(7, 0, 0, 0);    // 7 days


        _HostAlive = new ServiceHost(typeof(Alive), baseAddress);
        _HostAlive.AddServiceEndpoint(typeof(IAlive), tcpBinding, "alive");      // tcpBinding


        ServiceThrottlingBehavior throttle = _HostAlive.Description.Behaviors.Find<ServiceThrottlingBehavior>();
        if (throttle == null)
        {
            throttle = new ServiceThrottlingBehavior();
            host.Description.Behaviors.Add(throttle);
        }
        throttle.MaxConcurrentCalls = 1000;         // default 16
        throttle.MaxConcurrentInstances = 1000;     // default 26
        throttle.MaxConcurrentSessions = 1000;      // default 10 

        // open the host - bring it into life!
        host.Open();
Marcel
could you please just **update** your original question, instead of posting a dummy answer?? Thanks!
marc_s
You are using a callback contract. What's in that callback, and who is using the proxy? Is it a Winforms app, where the UI thread may be blocking while the callback is being executed (->deadlock)?
John
OK Marc I will update the question next time!John, it is a WPF app. The callback would normally be used but not in this case at all. I have two clients - one has a timer and repeatedly calles Alive() on its instance and gets back 'true'.The other client now calles AliveWait for 3 seconds and after 3 seconds the call comes back - all ok for this client. However, the first client on the timer has fired off 3 calls which were unanswered until the second client's call was finished! Clearly no callbacks are invoked nor is the Host doing anything!
Marcel
The proxy is being held onto so the Host can callback but none of this I do at this point!
Marcel
Found it myself - I didn't have the class decorated with , UseSynchronizationContext=false !!!
Marcel
+1  A: 

I found out what it was! I had forgotten to put the UseSynchronizationContext to false in the ServiceBehaviour of the Test class. This defaults to true which synchronises all calls on the service to run on the same thread! So any subsequent calls from other clients were simply queued!

Marcel