Silverlight and Synchronous Network Calls

Someone tweeted on Twitter that they wanted to stop the UI thread in Silverlight while they performed a couple of asynchronous network activities because one activity depended on the output of another.

To be honest, I don’t really like the idea and I’ve drawn this slide in the past;

image

where I’ve advised people not to try and subvert the asynchronous networking aspects of Silverlight so I suppose it’s no surprise that I’m not a big fan of trying to block the UI thread while networking activity completes.

Let’s say I’ve got this simple web service to invoke;

[ServiceContract]
public interface IService
{
  [OperationContract]
  string InitialCall();

  [OperationContract]
  string SubsequentCall(string input);
}

and all it does is return hard-coded strings;

public class Service : IService
{
  public string InitialCall()
  {
    return ("Hello");
  }

  public string SubsequentCall(string input)
  {
    return (input + " World");
  }
}

but to call it, I need to make 2 asynchronous trips across the network to invoke the InitialCall operation and then the SubsequentCall operation with the idea being that the second operation gets passed the results from the first one.

Now, in the Silverlight world the WCF proxy generation tools in Visual Studio will represent these asynchronous operations using the Event Based Asynchronous Pattern giving me an initial method to call XXXAsync and an EventArgs derived from AsyncCompletedEventArgs.

If I create a quick view;

<UserControl
  x:Class="SilverlightApplication5.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="d"
  d:DesignHeight="300"
  d:DesignWidth="400"
  xmlns:local="clr-namespace:SilverlightApplication5">
  <UserControl.DataContext>
    <local:ViewModel />
  </UserControl.DataContext>
  <Grid
    x:Name="LayoutRoot"
    Background="White">
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition
        Height="Auto" />
    </Grid.RowDefinitions>
    <Viewbox>
      <TextBlock
        Text="{Binding DisplayText}" />
    </Viewbox>
    <Button
      Grid.Row="1"
      Content="Click"
      Command="{Binding InvokeServiceCommand}" />
  </Grid>
</UserControl>

and a quick view model behind it;

  public class ViewModel : INotifyPropertyChanged
  {
    public ViewModel()
    {
      this.invokeServiceCommand = new SimpleCommand(
        OnInvokeService);
    }
    void OnInvokeService()
    {
      // Invoke the 2 service methods asynchronously and then
      // change the DisplayText property to reflect that.
    }
    public ICommand InvokeServiceCommand
    {
      get
      {
        return (this.invokeServiceCommand);
      }
    }
    SimpleCommand invokeServiceCommand;

    public string DisplayText
    {
      get
      {
        return (_DisplayText);
      }
      set
      {
        _DisplayText = value;
        RaisePropertyChanged("DisplayText");
      }
    }
    string _DisplayText;

    void RaisePropertyChanged(string property)
    {
      if (this.PropertyChanged != null)
      {
        this.PropertyChanged(this,
          new PropertyChangedEventArgs(property));
      }
    }
    public event PropertyChangedEventHandler PropertyChanged;
  }

( with SimpleCommand being just a simple implementation of ICommand ).

Then the question becomes one of how I can implement the OnInvokeService method with the minimum of pain. Here’s one approach;

    void OnInvokeService()
    {
      ServiceClient proxy = new ServiceClient();
      proxy.InitialCallCompleted += OnInitialCallCompleted;
      proxy.InitialCallAsync();
    }
    void OnInitialCallCompleted(object sender, InitialCallCompletedEventArgs e)
    {
      ServiceClient proxy = (ServiceClient)sender;
      proxy.InitialCallCompleted -= OnInitialCallCompleted;

      // TODO: Need a way to deal with the errors and cancellation.
      if (!e.Cancelled && (e.Error == null))
      {
        proxy.SubsequentCallCompleted += OnSubsequentCallCompleted;
        proxy.SubsequentCallAsync(e.Result);
      }
    }
    void OnSubsequentCallCompleted(object sender, SubsequentCallCompletedEventArgs e)
    {      
      ServiceClient proxy = (ServiceClient)sender;
      proxy.SubsequentCallCompleted -= OnSubsequentCallCompleted;

      // TODO: Ditto
      if (!e.Cancelled && (e.Error == null))
      {
        this.DisplayText = e.Result;
      }
    }

This is fairly repetitive but it’s fairly simple – we’re chaining together calls until they end. I find it’s actually easier to write explicit methods for the handlers because if you don’t and write anonymous methods then you have to keep a reference around to the delegate so that you can unsubscribe from the events.

Naturally, we could easily use something like a BusyIndicator to make some or all of our UI busy while these 2 asynchronous operations are in progress, taking care to make sure that we cancel the busy state in the case of all different kinds of completion (errors, cancellation, success). There’s no need for the user to “interfere” with the application while our async work is in flight, unless we explicitly want them to.

Alternatively, we could try and pretend that this call is synchronous. Perhaps something along the lines of;

    void OnInvokeService()
    {
      ServiceClient proxy = new ServiceClient();
      string result = string.Empty;

      AutoResetEvent evt = new AutoResetEvent(false);      

      proxy.InitialCallCompleted += (s, e) =>
        {
          proxy.SubsequentCallAsync(e.Result);
        };

      proxy.SubsequentCallCompleted += (s, e) =>
      {
        result += e.Result;
        evt.Set();
      };  

      proxy.InitialCallAsync();    

      evt.WaitOne();

      this.DisplayText = result;
    }

This initially looks like less hassle but this code isn’t going to work for us although it tries to code block the UI thread on line 21 until the two asynchronous operations complete.

The WCF proxy here is set up to be friendly towards us in that it attempts to deliver events like InitialCallCompleted back to the UI thread so that we don’t have to deal with being “on the wrong thread” for the completion of the event.

That’s a nice thing for the proxy to do for us.

However, it breaks our code here causing it to deadlock. There’s no way for the WCF proxy code to get back to our UI thread and ask it to dispatch messages because our UI thread is blocked in a call to WaitHandle.WaitOne and so it can’t dispatch messages which means that we deadlock and never invoke the lambda on line 10.

This deadlock is kind of interesting because while I’m deadlocked on that WaitOne I can exercise my browser and see how well it (IE 9) deals with this UI thread being blocked.

image

The effect of blocking the UI thread has been not only to hang the tab with the Silverlight application, it’s hung the whole browser. I cannot type into the address bar. I cannot browse to another site. The whole thing’s gone horribly wrong.

That’s not a criticism of IE – I shouldn’t have written code that blocked the UI thread. I was guided not to do it Smile

Now, it’s worth saying that if we weren’t using a WCF proxy but were, instead, using something that exposed the traditional Asynchronous Programming Model like the HttpWebRequest class for instance that does not try and marshal its asynchronous results back to the UI thread then we would not have hit our deadlock but we would still have blocked the UI thread for some period of time that isn’t really within our control and the user’s experience is poor.

I’d avoid it. I’d use the former asynchronous model and never try and pretend that we’re working synchronously when we’re not.

That said, there are lots of different ways that original code could have been written including some newer options…

Trying the Async CTP

If I was using the Async CTP then I might be able to make use of the new await construct. I had a stab at that;

    async void OnInvokeService()
    {
      ServiceClient proxy = new ServiceClient();

      var initialHandler = new TcsHandler<InitialCallCompletedEventArgs, string>(
          args => args.Result);

      proxy.InitialCallCompleted += initialHandler.Handler;
      proxy.InitialCallAsync();
      await initialHandler.TaskCompletionSource.Task;
      proxy.InitialCallCompleted -= initialHandler.Handler;

      var subsequentHandler = new TcsHandler<SubsequentCallCompletedEventArgs, string>(
        args => args.Result);

      proxy.SubsequentCallCompleted += subsequentHandler.Handler;
      proxy.SubsequentCallAsync(initialHandler.Result);
      await subsequentHandler.TaskCompletionSource.Task;
      proxy.SubsequentCallCompleted -= subsequentHandler.Handler;

      this.DisplayText = subsequentHandler.Result;
    }

This now looks fairly neat and tidy. It looks like synchronous code when it isn’t. The only problem I had in putting that together was how to go from the WCF proxy code which uses event handlers with arguments derived from AsyncCompletedEventArgs to something that matches the Async CTP’s awaitable pattern.

I tried to do that by relying on TaskCompletionSource and Task and I wrote a little class to help me with that – TcsHandler below. Perhaps there’s a better way?

  public class TcsHandler<E,T> where E : AsyncCompletedEventArgs
  {
    public TcsHandler(Func<E,T> selector)
    {
      this.TaskCompletionSource = new TaskCompletionSource<T>();
      this.selector = selector;
    }
    public T Result
    {
      get
      {
        return (this.TaskCompletionSource.Task.Result);
      }
    }
    public void Handler(object sender, E args)
    {
      if (args.Cancelled)
      {
        this.TaskCompletionSource.SetCanceled();
      }
      else if (args.Error != null)
      {
        this.TaskCompletionSource.SetException(args.Error);
      }
      else
      {
        this.TaskCompletionSource.SetResult(
          this.selector(args));
      }
    }
    public TaskCompletionSource<T> TaskCompletionSource
    {
      get;
      private set;
    }
    Func<E, T> selector;
  }

This little helper class would be easier to implement if AsyncCompletedEventArgs had a friend AsyncCompletedEventArgs<T> but it doesn’t and so I tried to solve the problem of;

“how does this class go from an instance of something derived from AsyncCompletedEventArgs to be able to call TaskCompletionSource<T>.SetResult

by introducing a simple projection function to the constructor that can be used to extract the data that’s needed.

Now…I’m pretty sure that when the Async CTP comes to fruition, the tools such as the proxy-generation tool for WCF services would be updated to use the Task Asynchronous Pattern rather than the Event based Asynchronous Pattern that they’re using here for Silverlight so things should get a lot easier.

Another option…

Trying Rx

If I wasn’t using the Async CTP but was perhaps using Rx then I could take a different approach to making these 2 asynchronous calls. Rx bridges nicely to the world of .NET events.

In this particular case though, I want to be able to bridge to the WCF proxy which is using the Event based Asynchronous Pattern and in each case I need to be able to;

  1. Invoke some initial operation to make the WCF call. For example – to call proxy.InitialCallAsync
  2. Harvest the results from some AsyncCompletedEventArgs derived class.

I’m not 100% whether there’s an easier way of doing this or whether I got it nearly correct but I made an attempt at something that tried to do that to produce an IObservable<T>;

    static IObservable<T> FromAsyncCompletedEventPattern<T>(
      Action initialAction,
      object source, string eventName) where T : AsyncCompletedEventArgs
    {
      return (
        Observable.Defer(
          () =>
          {
            var result =

            Observable.Create<T>(
              observer =>
              {
                IDisposable sub =
                  Observable
                    .FromEventPattern<T>(source, eventName)
                    .Subscribe(
                      value =>
                      {
                        if (value.EventArgs.Error != null)
                        {
                          observer.OnError(value.EventArgs.Error);
                        }
                        else if (value.EventArgs.Cancelled)
                        {
                          observer.OnCompleted();
                        }
                        else
                        {
                          observer.OnNext(value.EventArgs);
                        }
                      },
                      ex => observer.OnError(ex),
                      () => observer.OnCompleted());

                return (sub);
              });

            initialAction();

            return (result);
          }));
    }

I’m not at all sure that I got that correct but it allows me to then implement my OnInvokeService method as;

    void OnInvokeService()
    {
      ServiceClient proxy = new ServiceClient();

      var obs =
        FromAsyncCompletedEventPattern<InitialCallCompletedEventArgs>(
          () => proxy.InitialCallAsync(),
          proxy,
          "InitialCallCompleted")
        .SelectMany(result =>
          {
            return (
              FromAsyncCompletedEventPattern<SubsequentCallCompletedEventArgs>(
                () => proxy.SubsequentCallAsync(result.Result),
                proxy,
                "SubsequentCallCompleted"));
          })
        .Select(
          result => result.Result);
       
      obs.Subscribe(
        value => this.DisplayText = value);
    }

which feels fairly “clean” although I daresay there’s a few Rx gremlins lurking in the code I wrote.

Ultimately…

Those networking APIs all surface asynchronously for a reason. Trying to bend them to your evil ways Winking smile is only leading towards a world of pain so I’d recommend the traditional approach or the newer Async/Rx style approaches but, whatever you choose, don’t deliberately go and block that UI thread while the network calls complete.