none
Moving WCF off of UI thread - deadlocked RRS feed

  • Question

  • I would appreciate any help in resolving my deadlock situation; using C#, .net 4.5.1 and VS2013.  I'm relatively new to C# and GUI code so please be kind. ;)

    I have 2 processes communicating via WCF/http.  Process G renders a GUI and sends instructions to low level console process, process L.  Process L returns data to process G to be displayed.  In my deadlock case, process G must wait for data from process L before it can continue.  Process L has already executed the WCF call to send the data to process G, but the process G thread that processes WCF is apparently the main UI thread, which is the same thread that is stuck waiting for the result, so it deadlocks. 

    The most direct solution is to move the WCF processing off of the UI thread, but I'm not sure how to do this.  Or if I could temporarily free the UI thread to process the WCF call, then return to its previous location that would also work, but again I'm not sure how to do this.  I'm open to any and all suggestions! 

    Here is an example solution that shows exactly my problem, just build and run both processes.  Follow the on-screen instructions in the console window then stop the debugger and both processes will be at their deadlock locations.

    WCF Deadlock Example Solution

    Direct Download Link

    Here are the relevant source files in-line:

    Low level application L, comment shows where it gets stuck:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ServiceModel;
    
    
    namespace WCF_DataSupplier
    {
    
      [ServiceContract]
      public interface IWCF_GUI_Receiver
      {
        [OperationContract]
        string ReceiveData(bool bContinue);
      }
    
      [ServiceBehavior(
         IncludeExceptionDetailInFaults = true,
         ConcurrencyMode = ConcurrencyMode.Multiple,
         InstanceContextMode = InstanceContextMode.Single
       )]
    
      
      class Program
      {
        static void Main(string[] args)
        {
            // Make a connection to the UI process
            ChannelFactory<IWCF_GUI_Receiver> httpFactory = new ChannelFactory<IWCF_GUI_Receiver>(
                                                           new BasicHttpBinding(),
                                                           new EndpointAddress("http://localhost:60001/ReceiveData"));
    
            IWCF_GUI_Receiver resultsReceiver = httpFactory.CreateChannel();
    
          
          Console.WriteLine("Press X to initiate the deadlock, then break in with the debugger and view the main thread in each process to see why we're deadlocked.");
          while (true)
          {
            System.Threading.Thread.Sleep(1000);
            if (Console.KeyAvailable == true)
            {
              if(Console.ReadKey().Key.ToString().ToUpper() == "X")
              {
                try
                {
                  // This process is stuck here, waiting for the other process to "process" this call to ReceiveData(), 
                  // unfortunately the other process is stuck in a loop that depends on this data sent to ReceiveData()
                  resultsReceiver.ReceiveData(true);
                }
                catch(Exception ex)
                {
                  Console.WriteLine("Exception: " + ex.Message);
                }
              } // if()
            } // if()
          } // while
        } // Main()
      } // class
    } // namespace
    

    GUI application, comments show where it gets stuck:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using System;
    using System.ServiceModel;
    
    
    
    namespace WCF_GUI
    {
    
      [ServiceContract]
      public interface IWCF_GUI_Receiver
      {
        [OperationContract]
        string ReceiveData(bool bContinue);
      }
    
      [ServiceBehavior(
         IncludeExceptionDetailInFaults = true,
         ConcurrencyMode = ConcurrencyMode.Multiple,
         InstanceContextMode = InstanceContextMode.Single
       )]
    
    
      public partial class Form1 : Form, IWCF_GUI_Receiver
      {
        ServiceHost serviceHost;
        bool bReceivedInput = false;
    
        void WaitForInput()
        {
          // This process stuck here waiting for bReceivedInput to change state, which will happen as soon as ReceiveData() is executed.  
          // Unfortunately this stuck thread is the same thread that is to execute ReceiveData(), so we're deadlocked. 
          while (bReceivedInput == false)
          {
            System.Threading.Thread.Sleep(200);
          } // while()
    
          textBox1.Text = "Changed!";
    
        } // WaitForInput()
    
    
        public string ReceiveData(bool bContinue)
        {
          bReceivedInput = true;
          return "OK";
        }
    
        public Form1()
        {
          try
          {
            serviceHost = new ServiceHost(this, new Uri("http://localhost:60001"));
            serviceHost.AddServiceEndpoint(typeof(WCF_GUI.IWCF_GUI_Receiver), new BasicHttpBinding(), "ReceiveData");
            serviceHost.Open();
          }
          catch (Exception ex)
          {
            Console.WriteLine("serviceHost.Open exception: " + ex.Message);
          }
    
          InitializeComponent();
    
          WaitForInput();
    
        } // Form1()
      } // class
    } // namespace
    


    Wednesday, November 19, 2014 6:54 PM

Answers

  • Just to close the thread;  I tried putting the service proxy on a different thread.  That resolved the deadlock, but then I was getting an exception because the callback was trying to access data owned by the main UI thread.  So I tried then using BeginInvoke to get access to the UI thread data, but then I was right back on the UI thread and back to square one with a deadlock.

    In the end, I created a timer and an event that polls the service.  At the place of the deadlock instead of busy waiting I start the timer and allow the UI thread to return.  Now the UI thread will poll the service then immediately return so it can process any waiting callbacks by the service.  When the polling method returns the status indicating it's OK to continue, I stop the timer and open the next part of the UI and everything continues as it should.

    It's a bit of a hack, but it will have to do for now.  Maybe in rev 2 of the product we can redesign this whole thing and make it better all around. 

    Thanks everyone for the help!

    Friday, November 21, 2014 9:54 PM

All replies

  • WCF services default to single threaded. All calls and callbacks get marshalled to a single thread (or SynchronizationContext to be more accurate).

    My app is a single threaded WPF app, so the SynchronizationContext gets set to the dispatch thread.

    When the callback comes in it tries to marshal the call to the dispatch thread, which of course is sat blocking on the original service call. I'm not clear it locks exactly, but there's obviously some global lock that it tries to get before waiting for the dispatch thread.

    When the dispatch thread then calls the service again, it deadlocks on this global lock.

    Two ways around it:

    1) Create the service proxy on a different thread in the first place. All calls will get marshalled through this thread instead and it won't matter that the dispatch thread is blocked.

    2) Apply [CallbackBehavior(UseSynchronizationContext = false)] attribute to the client class that implements the callback. This means WCF will ignore the synchronisation context when the callback comes in, and it will service it on any available thread.

    I went with 2. Obviously this means I need to marshal callbacks that could update the GUI to the dispatch thread myself, but luckily my callback implementation is a small wrapper anyway, so I just do a _dispatcher.BeginInvoke() in each callback method to marshal ASYNCHRONOUSLY. The dispatch thread will then service when it gets a chance which is what I wanted in the first place.

    Thursday, November 20, 2014 8:52 AM
  • I have 2 processes communicating via WCF/http.  Process G renders a GUI and sends instructions to low level console process, process L. 

    Well, you need to learn how to develop solutions that have a speration of concerns, which means that the UI should never be making direct calls to the service and another part of the solution the Service Layer makes the calls on the behalf of the UI layer.  You need to learn how to use a MVP pattern and N-Tier pattern.

    Maybe in this situation the calls to the WCF service should be using async and the UI should be spwanig an async thread so it doesn't wait for a response.

    It doesn't matter about what type of UI uses MVP. It can be ASP.NET, Windows, WPF or Sliverlight form solution using MVP it all works the same.

    http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter

    http://polymorphicpodcast.com/shows/mv-patterns

    It doesn't matter any solution can use N-Tier ASP.NET, Windows form, WPF or Silverlight UI, which is the seperation of concerns and each layer has its distinct resposnsibilities.

    http://en.wikipedia.org/wiki/Multitier_architecture

    http://msdn.microsoft.com/en-us/library/hh191443.aspx

    http://msdn.microsoft.com/en-us/library/ms730059(v=vs.110).aspx

    Maybe, you doen't have a 3 tier, and you only have a 2 tier.

    Thursday, November 20, 2014 2:53 PM
  • Thanks both of you for the replies.  I thought I had a 3 tier solution.  I explicitly spawn new threads for calling from the UI down to the service.  But I was caught off guard by the WCF calls back to the UI being executed in the UI thread, I thought these would be given their own thread in the UI.  So you're right, I essentially have a 2 tier solution when the service calls back to the UI.  I'll try the suggestions above to see if I can get the WCF calls from the service back to the UI to execute in a separate thread on the UI side.
    • Edited by Matt_FL Thursday, November 20, 2014 6:48 PM Meant WCF instead of WPF, corrected this.
    Thursday, November 20, 2014 5:10 PM
  • If using WPF, then maybe you should look into the MVVM pattern or soon to be my favorite the MVPVM,  the sperations of concerns breaking the tight coupling of the UI.

    http://msdn.microsoft.com/en-us/magazine/hh580734.aspx

    http://www.codeproject.com/Articles/88390/MVP-VM-Model-View-Presenter-ViewModel-with-Data-Bi

    Thursday, November 20, 2014 6:20 PM
  • Oops, I meant WCF, I edited my message.  Thanks again!
    Thursday, November 20, 2014 6:47 PM
  • Just to close the thread;  I tried putting the service proxy on a different thread.  That resolved the deadlock, but then I was getting an exception because the callback was trying to access data owned by the main UI thread.  So I tried then using BeginInvoke to get access to the UI thread data, but then I was right back on the UI thread and back to square one with a deadlock.

    In the end, I created a timer and an event that polls the service.  At the place of the deadlock instead of busy waiting I start the timer and allow the UI thread to return.  Now the UI thread will poll the service then immediately return so it can process any waiting callbacks by the service.  When the polling method returns the status indicating it's OK to continue, I stop the timer and open the next part of the UI and everything continues as it should.

    It's a bit of a hack, but it will have to do for now.  Maybe in rev 2 of the product we can redesign this whole thing and make it better all around. 

    Thanks everyone for the help!

    Friday, November 21, 2014 9:54 PM