title: deadlock when using WCF Duplex Polling with Silverlight
body: <p>I have followed Tomek Janczuk's demonstration on silverlight tv to create a chat program that uses WCF Duplex Polling web service. The client subscribes to the server, and then the server initiates notifications to all connected clients to publish events. </p>
  
  <p>The Idea is simple, on the client, there is a button that allows the client to connect. A text box where the client can write a message and publish it, and a bigger text box that presents all the notifications received from the server.</p>
  
  <p>I connected 3 clients (in different browsers - IE, Firefox and Chrome) and it all works nicely. They send messages and receive them smoothly. The problem starts when I close one of the browsers. As soon as one client is out, the other clients get stuck. They stop getting notifications. </p>
  
  <p>I am guessing that the loop in the server that goes through all the clients and sends them the notifications is stuck on the client that is now missing. I tried catching the exception and removing it from the clients list (see code) but it still does not help.</p>
  
  <p>any ideas? </p>
  
  <p>The server code is as follows:</p>
  
  <pre><code>    using System;
  using System.Linq;
  using System.Runtime.Serialization;
  using System.ServiceModel;
  using System.ServiceModel.Activation;
  using System.Collections.Generic;
  using System.Runtime.Remoting.Channels;
  
  namespace ChatDemo.Web
  {
      [ServiceContract]
      public interface IChatNotification 
      {
          // this will be used as a callback method, therefore it must be one way
          [OperationContract(IsOneWay=true)]
          void Notify(string message);
  
          [OperationContract(IsOneWay = true)]
          void Subscribed();
      }
  
      // define this as a callback contract - to allow push
      [ServiceContract(Namespace="", CallbackContract=typeof(IChatNotification))]
      [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
      [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
      public class ChatService
      {
          SynchronizedCollection&lt;IChatNotification&gt; clients = new SynchronizedCollection&lt;IChatNotification&gt;();
  
          [OperationContract(IsOneWay=true)]
          public void Subscribe()
          {
              IChatNotification cli = OperationContext.Current.GetCallbackChannel&lt;IChatNotification&gt;();
              this.clients.Add(cli);
              // inform the client it is now subscribed
              cli.Subscribed();
  
              Publish("New Client Connected: " + cli.GetHashCode());
  
          }
  
          [OperationContract(IsOneWay = true)]
          public void Publish(string message)
          {
              SynchronizedCollection&lt;IChatNotification&gt; toRemove = new SynchronizedCollection&lt;IChatNotification&gt;();
  
              foreach (IChatNotification channel in this.clients)
              {
                  try
                  {
                      channel.Notify(message);
                  }
                  catch
                  {
                      toRemove.Add(channel);
                  }
              }
  
              // now remove all the dead channels
              foreach (IChatNotification chnl in toRemove)
              {
                  this.clients.Remove(chnl);
              }
          }
      }
  }
  </code></pre>
  
  <p>The client code is as follows:</p>
  
  <pre><code>void client_NotifyReceived(object sender, ChatServiceProxy.NotifyReceivedEventArgs e)
  {
      this.Messages.Text += string.Format("{0}\n\n", e.Error != null ? e.Error.ToString() : e.message);
  }
  
  private void MyMessage_KeyDown(object sender, KeyEventArgs e)
  {
      if (e.Key == Key.Enter)
      {
          this.client.PublishAsync(this.MyMessage.Text);
          this.MyMessage.Text = "";
      }
  }
  
  private void Button_Click(object sender, RoutedEventArgs e)
  {
      this.client = new ChatServiceProxy.ChatServiceClient(new PollingDuplexHttpBinding { DuplexMode = PollingDuplexMode.MultipleMessagesPerPoll }, new EndpointAddress("../ChatService.svc"));
  
      // listen for server events
      this.client.NotifyReceived += new EventHandler&lt;ChatServiceProxy.NotifyReceivedEventArgs&gt;(client_NotifyReceived);
  
      this.client.SubscribedReceived += new EventHandler&lt;System.ComponentModel.AsyncCompletedEventArgs&gt;(client_SubscribedReceived);
  
      // subscribe for the server events
      this.client.SubscribeAsync();
  
  }
  
  void client_SubscribedReceived(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
  {
      try
      {
          Messages.Text += "Connected!\n\n";
          gsConnect.Color = Colors.Green;
      }
      catch
      {
          Messages.Text += "Failed to Connect!\n\n";
  
      }
  }
  </code></pre>
  
  <p>And the web config is as follows:</p>
  
  <pre><code>  &lt;system.serviceModel&gt;
      &lt;extensions&gt;
        &lt;bindingExtensions&gt;
          &lt;add name="pollingDuplex" type="System.ServiceModel.Configuration.PollingDuplexHttpBindingCollectionElement, System.ServiceModel.PollingDuplex, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/&gt;
        &lt;/bindingExtensions&gt;
      &lt;/extensions&gt;
      &lt;behaviors&gt;
        &lt;serviceBehaviors&gt;
          &lt;behavior name=""&gt;
            &lt;serviceMetadata httpGetEnabled="true"/&gt;
            &lt;serviceDebug includeExceptionDetailInFaults="false"/&gt;
          &lt;/behavior&gt;
        &lt;/serviceBehaviors&gt;
      &lt;/behaviors&gt;
      &lt;bindings&gt;
        &lt;pollingDuplex&gt;        
          &lt;binding name="myPollingDuplex" duplexMode="MultipleMessagesPerPoll"/&gt;
        &lt;/pollingDuplex&gt;
      &lt;/bindings&gt;
      &lt;serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true"/&gt;
      &lt;services&gt;
        &lt;service name="ChatDemo.Web.ChatService"&gt;
          &lt;endpoint address="" binding="pollingDuplex" bindingConfiguration="myPollingDuplex" contract="ChatDemo.Web.ChatService"/&gt;
          &lt;endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/&gt;
        &lt;/service&gt;
      &lt;/services&gt;
    &lt;/system.serviceModel&gt;
  </code></pre>