If you work with WPF or Silverlight then you really should check out Rob Eisenberg presentation at MIX10 titled Build Your Own MVVM Framework. In that presentation he goes through a sample application that utilizes no code behind and no data binding or command binding expressions, all is hooked up through conventions and is driven solely from the presentation model. The sample app has about 500 lines of framework or infrastructure code that handles the view look up and convention based binding. I urge you to download it and really dig through it, it contains some awesome code and ideas.

But I will focus this blog post on one aspect of that sample app which is how Rob implemented async workflows. It might be a long post with a lot of code :)

Sequential async workflow:

public IEnumerable<IResult> ExecuteSearch()
{
        var search = new SearchGames
        {
                SearchText = SearchText
        }.AsResult();

        yield return Show.Busy();
        yield return search;

        var resultCount = search.Response.Count();

        if (resultCount == 0)
                SearchResults = _noResults.WithTitle(SearchText);
        else 
                SearchResults = _results.With(search.Response);

        yield return Show.NotBusy();
}

Before I dig into what the above code does and how it works I need to explain the problem. Anyone who has worked with WinForms/WPF/Silverlight knows that backend calls and other long running work needs to be done on a background thread. However all UI updates needs to be done on the UI thread. If you use ThreadPool.QueueUserWorkItem and you want to notify the UI that something has updated (issue a NotifyPropertyChanged for example) then you need to marshal the the call via the WPF dispatcher so the NotifyPropertyChanged happens on the UI thread. Another option is to use the BackgroundWorker that automatically executes the event RunWorkerCompleted on the same thread that called RunWorkerAsync.

Example:

private void ExecuteSearch()
{
      IsBusy = true;

        var worker = new BackgroundWorker();
        
        IEnumerable<SearchResult> results;
        
        worker.DoWork += (e, sender) =>
        {
                results = searchService.SearchGames(SearchText);
        };
        
        worker.RunWorkerCompleted += (e, sender) =>
        {
                if (results.Count == 0)
                        SearchResults = _noResults.WithTitle(SearchText);
                else 
                        SearchResults = _results.With(results);
                        
                IsBusy = false;        
        };

        worker.RunWorkerAsync();
}

This does not look so bad, we still have all the code in one method thanks to the lambdas. But imagine that we need to fetch the game (another backend call) and open the game screen if the returned search result only match exactly one game. In that case we would need to new up another BackgroundWorker inside the RunWorkerCompleted lambda and then hookup DoWork and RunWorkerCompleted. The code would be very messy (nested lambda callbacks) and it would be hard to read the sequential flow of the process.

Coroutines to the rescue. Wikipedia definition reads:

Coroutines are program components that generalize subroutines to allow multiple entry points for suspending and resuming execution at certain locations
But C# does not have that! Well it kind of does. You can get the same effect if you define an enumerator using yield returns. In a method that returns IEnumerable you can use yield return. The C# compiler will turn that method into a state machine (to implement the enumerator). Each time someone calls MoveNext (which happens once each foreach loop) the execution of the method will continue until the next yield return.

Example:

public IEnumerable<string> GetStrings()
{
    yield return "Hello";
    
    Console.WriteLine("execution continued");
    
    yield return "Good bye";
    
    Console.WriteLine("Nothing left");
}

This method will be turned into a MoveNext method on a generated class that implements IEnumerator:

private bool MoveNext()
{
    switch (this.<>1__state)
    {
        case 0:
            this.<>1__state = -1;
            this.<>2__current = "Hello";
            this.<>1__state = 1;
            return true;

        case 1:
            this.<>1__state = -1;
            Console.WriteLine("execution continued");
            this.<>2__current = "Good bye";
            this.<>1__state = 2;
            return true;

        case 2:
            this.<>1__state = -1;
            Console.WriteLine("Nothing left");
            break;
    }
    return false;
}

Ok, so now that we know how yield works we can use that to get something like Coroutines.

public IEnumerable<IResult> ExecuteSearch()
{
   // yield returns...
}

public interface IResult
{
   void Execute(); 
   event EventHandler Completed;
} 

Back to Robs sample app. The ExecuteSearch method above returns an IEnumerable of IResult, an interface that has an Execute method and a Completed event. The magic happens in how this enumerable is consumed, which is handled by the ResultEnumerator class:

public class ResultEnumerator
{
        private readonly IEnumerator<IResult> _enumerator;

        public ResultEnumerator(IEnumerable<IResult> children)
        {
                _enumerator = children.GetEnumerator();
        }

        public void Enumerate()
        {
                ChildCompleted(null, EventArgs.Empty);
        }

        private void ChildCompleted(object sender, EventArgs args)
        {
                var previous = sender as IResult;

                if(previous != null)
                        previous.Completed -= ChildCompleted;

                if(!_enumerator.MoveNext())
                        return;

                var next = _enumerator.Current;
                next.Completed += ChildCompleted;
                next.Execute();
        }
}

This class might take some time to get your head around. What happens is that we take the first IResult by calling MoveNext, we then hook up the Completed event and then call the Execute method. The interesting thing is that the Completed event is hooked up the the same method that we are in. The result is that the first IResult controls when the next IResult is fetched (the next IResult won't be fetched until the one before executes the Completed event). Ok, think about this until you understand it :)

Now lets look at how we can use this. Lets try to create a IResult that executes a lambda in a background thread then executes the Completed event on the UI thread.

public class BackgroundResult : IResult
{
        private readonly Action action;

        public BackgroundResult(Action action)
        {
                this.action = action;
        }

        public void Execute()
        {
                var backgroundWorker = new BackgroundWorker();
                backgroundWorker.DoWork += (e, sender) => action();
                backgroundWorker.RunWorkerCompleted += (e, sender) => Completed(this, EventArgs.Empty);
                backgroundWorker.RunWorkerAsync();
        }

        public event EventHandler Completed = delegate { };
}

Lets look at our example that used BackgroundWorker but rewrite it to use our new BackgroundResult:

private IEnumerable<IResult> ExecuteSearch()
{
    IsBusy = true;
    
    IEnumerable<SearchResult> results;

    yield return new BackgroundResult(() => 
    {
        results = searchService.Search(SearchText);
    });
    
    if (results.Count == 0)
       SearchResults = _noResults.WithTitle(SearchText);
    else 
       SearchResults = _results.With(results);
                
    IsBusy = false;            
}

Looks better doesn't it? The BackgroundResult will block the execution of the method until the action completes which will fire the Completed event (which happens on the UI thread) which in turn will resume the execution of the ExecuteSearch method. The requirement to show the game screen when there is only one match is now much easier to handle as we can yield another BackgroundResult to fetch the game. The method logic happens in the sequential order that it is written, making it much easier to read, test and debug.

Now to the complete version of the method from Robs example app:

public IEnumerable<IResult> ExecuteSearch()
{
        var search = new SearchGames
        {
                SearchText = SearchText
        }.AsResult();

        yield return Show.Busy();
        yield return search;

        var resultCount = search.Response.Count();

        if (resultCount == 0)
                SearchResults = _noResults.WithTitle(SearchText);
        else if (resultCount == 1 && search.Response.First().Title == SearchText)
        {
                var getGame = new GetGame
                {
                        Id = search.Response.First().Id
                }.AsResult();

                yield return getGame;
                yield return Show.Screen<ExploreGameViewModel>()
                        .Configured(x => x.WithGame(getGame.Response));
        }
        else SearchResults = _results.With(search.Response);

        yield return Show.NotBusy();
}

In the complete version he handles the case where there is only one match, in that case GetGame query is also issued to the backend. Before I end this post I will just highlight another interesting aspect of this sample and that is how Rob handles backend interaction. Instead of directly calling a IBackend service he returns an IQueryResult or ICommandResult which will handle the execution of the backend call and the thread marshalling. This improves the testability as the method will not require any mocking to test.

It's not trivial stuff, it takes some time to get your head around how the ResultEnumerator works and what it makes possible. But I think it is a pretty brilliant solution to keep complex async workflows in the same place and be able to read them sequentially. I am not sure if it is the same thing but if I remember correctly I think the C# team is looking at making this easier in C# 5 with an async keyword. Does anyone know how this implementation of coroutines compares to the async features in F#?

Check out the full sample application

6 comments:

Ajai Shankar said...

Think this is along the same lines (http://easyasync.codeplex.com) but more on async io side.

Torkel said...

Yes, I also got a tip from twitter that Dream MindTouch uses sonmething similar:

http://developer.mindtouch.com/en/docs/Dream/Tutorials/Using_Coroutines_for_Asynchronous_programming

Justin Chase said...

That's pretty sweet, I wrote a couple of post on almost the same thing a while back:

Asynchronous Programming With Iterators

And my interpretation of it:
Simple synchronization with Iterators in C#

Except that I think that what you have here might be even better. I like! I will definitely watch that video thanks for the link.

bjorg said...

Getting started with doing coroutines is quite easy, but getting it right is very hard. In MindTouch Dream, we spent some considerable time making sure that 1) coroutines without results are reported as errors, 2) exceptions are propagated as expected to an outer context (or can be caught in place), and 3) exception stack traces capture the coroutine stack trace as well. That's a lot machinery and without it, programming coroutines becomes insanely complicated. If you want to learn more, check out MindTouch Dream, which is open source and available under Apache 2.0 license. Enjoy!

Adam said...

I was playing arround with this some and was wondering about a way to turn something like this into an extention method. I'v got a Datalayer that returns mostly List. I know that I can return an enumerable from that like

IEnumerable customers = DataLayer.GetCustomrers().AsEnumerable()

So how would I go about doing something like:

Async> customers = DataLayer.GetCustomers().ExecureAsync()
yield return customers

What I really want is to make several calls on diferent thread concurrently using my existing DataLayer.

Great Post by the way :)

-Adam

Anonymous said...

The Concurrency and Coordination Runtime (CCR) also has a similar coroutine async programming paradigm (iterators), and also includes robust exception handling (causalities). Check out http://thevalerios.net/matt/2008/11/exception-handling-queuing-and-ui-synchronization-for-wcf-services-using-the-ccr/