Tuesday, March 23, 2010

Sequential Asynchronous Workflows in Silverlight using Coroutines

It all began with a post I read in the Silverlight.Net forums. A user, new to Silverlight, was frustrated with the asynchronous programming model required by Silverlight and wondered how to make all of the calls synchronous. I admit I heavily resisted the notion because I think the asynchronous programming model is one that developers should learn and be comfortable with. Forcing an asynchronous call to "look synchronous" seems to involve a lot of framework overhead just to keep the developer from having to learn something new, and would limit their ability to take advantage of the functionality available.

Then I watched Rob Eisenberg's Build your own MVVM Framework presentation and learned what he was doing with a concept known as coroutines. That was the "ah-hah" moment: some beginning programmers truly are looking for a crutch and should take some time to embrace the asynchronous model and learn it. Other developers have embraced the model but are faced with an interesting challenge: how to keep code maintainable and readable when it is littered with asynchronous calls?

Download the source for this post Note: this requires Wintellect's PowerThreading library. Download that here, then follow the instructions on the download link to add the reference.

Consider for a moment a typical workflow in a service-driven Silverlight application. The application must fetch information from a service, then perhaps take that data and push it to another service, then take even more data and bring it back. You might be Simplifying Asynchronous Calls using Action, but the code can start to look a little interesting.

You really end up with two solutions. One is to wire in the calls with methods, like this (consider a scenario where we get a number from a service, and the previous call result feeds into the next call):

private int n1, n2; 

private void FirstCall()
{
    Service.GetRandomNumber(10, SecondCall);
}

private void SecondCall(int number)
{
    n1 = number; 
    Service.GetRandomNumber(n1, ThirdCall);
}

private void ThirdCall(int number)
{
    n2 = number; 
    // etc
}

Or you can go lambda expression/delegate crazy like this:

private void FetchNumbers()
{
    int n1, n2; 

    Service.GetRandomNumber(10, result =>
                                    {
                                        n1 = result;
                                        Service.GetRandomNumber(n1, secondResult =>
                                                                        {
                                                                            n2 = secondResult;
                                                                        });
                                    });
}

That may be somewhat manageable, but what if your workflow chains, say, a dozen calls together?

You don't want your asynchronous calls to be synchronous. You just want to make them sequential.

Does that point make sense? If and when you become comfortable with asynchronous programming, you understand the issues with blocking threads and waiting for calls and everything else. That's not the issue. You just want clean code that does the job. Something like this:

private void FetchNumbers()
{
   int n1 = Service.GetRandomNumber(10); 
   int n2 = Service.GetRandomNumber(n1);
}

Let that run. We understand it's asynchronous and don't want to block everything else, but please, please, let's just do it sequentially and make it easy to read, mmm kay?

This is where the power of coroutines comes in.

C# does not have direct support for coroutines.

Let's make that clear up front. We have ways to build frameworks that make them happen, but it's not an "out-of-the-box" implementation. But let's start with the basics: what is a coroutine, anyway?

When I learned about coroutines, I understood the concept immediately. What frustrated me was understanding the mechanism to apply them in C#. Most of the posts and topics I uncovered assumed a fairly advanced base of knowledge and often jumped into a solution with a framework and didn't describe the framework itself. I consider myself a fast learner and the fact that I was hitting my head against a brick wall and failing to find some decent "101" tutorials put me on a mission: first, build my own framework from scratch to prove to me that I truly understand coroutines in C#, and second, blog about it so that you, too, can benefit from the learning process to receive better understanding and have an easy set of references to learn about them.

Please use the comments below to let me know if I succeeded!

Note: while I did build my own framework to gain understanding, the example here uses an existing, free library that has been maintained for years. It has been thoroughly tested for stability and performance and is chock full of additional features and benefits ... I'll share more on that in a bit.

In summary, a coroutine is a subroutine or method with multiple entry and exit points that maintains state between calls. That is me paraphrasing the various definitions there. The two keys to a coroutine are that you may enter it from places other than the beginning, and that it retains state when you enter it again after exiting.

When I mentioned that C# doesn't directly support coroutines, I wasn't 100% correct. C# has native support for a very specialized form of a coroutine, known as an iterator. You use these all of the time in your code. Traditional thought is that a foreach loop really just takes a list, like a piece of tape, and then spins through the tape, right? We really just take an index, then increment the index to look at item[0] and item[1] and we're on our merry way, right?

Not quite. The most common uses of iterators work that way, but behind the scenes something far more complex is happening. Whenever you use an iterator (which is really any time you use the keyword foreach), you are invoking a state machine under the covers.

If this is news to you, stop right now and head over to my article, What's in Your Collection Part 3 of 3. Don't worry, parts 1 and 2 just cover basic collections. This part focuses on iterators and shows how an iterator is really a state machine.

Those of you already familiar with the yield command know this. For an example of exploiting this knowledge, take a look at Pipeline and Yield in C#. Once you understand the iterator is a state machine, you begin to realize the possibilities. You don't always have to have the full list, for example. You can generate it on the fly! This way you only create objects in the list when and as you need them.

So this is great, we understand a little bit about coroutines and iterators in C#, but what does that have to do with "sequential asynchronous workflows in Silverlight using coroutines?" A lot, actually.

You see, we can take advantage of the iterator state machine to build our own coroutines. The way we do that is by taking over the IEnumerator implementation required to perform iterations.

Take a moment to look at the IEnumerator documentation. Now, stop thinking of foreach as a simple loop, and instead, consider it a state machine. You climb into the state machine, and the rules are simple:

  1. We're currently at Current. In fact, we're stuck here until MoveNext is called.
  2. Only a call to MoveNext will let us advance. If the call is true, we are now at a new Current state, so back to step 1. If the call is false, we're done. We can either go home, or Reset and start all over again.

The only caveat is that MoveNext can really do a number of things, until it hits a yield statement. Once the yield statement is reached, the process stops until the next MoveNext is called.

Those are some pretty simple rules, aren't they? Armed with that knowledge, you should now be able to predict what will happen in this simple little routine:

public void PrintAll()
{
   foreach(int x in IntegerList())
   {
      Console.WriteLine(x); 
   }
}

public IEnumerable<int> IntegerList()
{
   yield return 1;
   Thread.Sleep(1000);
   yield return 2; 
   yield return 3; 
}

In this program, you'll see 1, then wait a second, then see 2 and 3, then hang. Yes, hang: the program will not end. Remember our rule that we keep doing "something" until we hit a yield statement. In the state machine above, we return 1 and save our state. When the loop returns, we call move next. This results in jumping into our spot right after the yield, where we sleep, then return 2. After we return 3, the foreach loop still asks for something more. The state machine hits the end of the yield statement, and returns ... never finding anything. Because yield isn't hit, the machine just sits there, waiting.

To rectify the situation, we add a "yield break" to the end. This will result in the MoveNext call returning false and let our state machine know we're out of states. Then, unless we reset the enumerator, we're done.

Solving the Problem with Coroutines

Let me restate the problem: we have asynchronous calls that we want to process sequentially without having to chain dozens of methods or resort to complex lambda expressions. How do we do it?

The trick lies within our state machine. Because the state machine is linked to two key events: MoveNext and the yield keyword, we can hook into the enumerator and force the state machine to wait for our asynchronous actions to complete. We do this without blocking other threads: everything else still happens asynchronously, we just have a "sequential view" of our workflow.

The first step is to consider a generic contract we'll use to navigate the workflow. This is similar to Rob's but I chose to use a delegate instead of an event, as I don't need multiple subscribers to the workflow. This is a very simple interface, but it's important to note something: the Yield method is what is called for the class to start it's "life" in the state machine. The Yielded delegate should be called when it's done. If I have an instant action, then yield might be:

() => { action(); Yielded(); }

So you enter through one function, but call the other. Who sets the Yielded action? Our enumerator, or the "state machine manager" that is taking care of everything. Here's the interface:

public interface ICoroutine
{
    void Yield();
    Action Yielded { get; set; } 
}

Note: I'm using this interface to help conceptualize the model. We won't actually use this interface in the final solution. It helps here with understanding the general flow.

Before we worry about an implementation, let's conceptualize the engine that drives this, I'll call the engine CoroutineManager. This is the "master of the universe" that controls the state engine. Let me show the idea to you first, then we'll talk about what it does:

CoroutineManager

Begin: Yield()    

Yield: Move Iterator
       At End? 
          Yes: Done 
       No: Grab next item
       Set next item "Yielded" action to point to local "Yield" action
       Call "Yield" on item 

OK, let's break it down. This is driving a state machine. The method will encapsulate our workflow. Each item in the workflow gets passed into the coroutine engine, does it's thing, then exits and moves to the next workflow item.

To help us enter our workflow, we have a Begin method to start the process. This simply calls into the Yield, which is the main routine.

Now we play a game of back-and-forth using yield and yielded. The manager's yield method moves the iterator along. If there is nothing left, it stops. Our workflow is finished. Otherwise, it does two interesting things. First, it hooks up the next instance to call back to the engine by setting the Yielded action. While this is done sequentially, it also happens asynchronously. I prove that in the sample code by having several workflow happen simultaneously. We'll get to that in a second.

Once the callback is wired in, it calls into the item to execute. Note that our enumerator is now "hanging" and cannot go anywhere until we come back into the Yielded action, and then it will go to look for another yield in the code.

Conceptually, it looks something like this ... here our workflow has some miscellaneous code, then yield returns an ICoroutine instance, then has some more code, etc. Keep in mind the "hook" into yielded happens based on an asynchronous event completing, or a direct call:

So how do we use this engine, now that we have it defined?

The Wintellect PowerThreading Library

Wintellect's Jeffrey Richter has built a PowerThreading Library that does exactly what we needed. The Silverlight version is lightweight (under 30KB) but contains some powerful functionality. I'll let you drive through the full capabilities of the library; we're going to focus on two key aspects.

First, the AsyncEnumerator class is what helps us implement the ICoroutine concept. This class allows for asynchronous sequential enumeration of any process that implements the IAsyncResult interface. Next, we'll use the EventAPMFactory to cast event-driven processes into an asynchronous result. Because some of our code uses action callbacks instead of events, we'll also take advantage of a simple event wrapper that allows any process to fire a completed event.

You can find the download link for the PowerThreading library, complete with documentation and examples, here.

First, the class that allows any action to become an event:

public class EventWrapper
{        
    public EventWrapper()
    {
        
    }

    public EventWrapper(Action action)
    {
        Begin = action;
    }

    public Action Begin { get; set; }

    public void End()
    {
        if (Completed != null)
        {
            Completed(this, EventArgs.Empty);
        }
    }

    public event EventHandler Completed; 
}

Fairly straightforward: set the start action, and make sure you call End and it will raise the Completed event.

For the sample program, I included several layers so you can see both the sequential workflow, but understand it is still truly asynchronous because other workflows are also executing at the same time. Here's the premise:

First, I simulate a "random number" routine that gets passed a maximum value and returns a random number. We pretend this is a service call and even build in a random delay so it doesn't return immediately. In my main page, I'm going to do a few workflow tasks like animating a text box, etc. Then, I make a square 400 x 400. I get a random number from the "service" with a max of 400, and draw that square. If the square has a size of 320 x 320, I then get another random number between 0 and 320. The result is subsequently smaller squares until we hit zero.

The signature of the random number routine is:

...
void GenerateRandomNumber(int max, Action<int> result); 
...

To make it even more interesting, the squares themselves use a workflow to get random numbers to set their fill color. This is interesting because they use the same random number generator with the delay built in, so each square may take several seconds before it gets a color. The reason I did this is so you can see the squares being drawn and colored at the same time: proof we are asynchronously stepping through each workflow, however the individual workflows themselves are firing sequentially.

Here is the XAML for a square:

<UserControl x:Class="Coroutines.SquareView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    x:Name="SquareHost"
    >
    <Grid x:Name="LayoutRoot" Background="Black"
          HorizontalAlignment="Center"
          VerticalAlignment="Center">
        <Rectangle x:Name="Square" 
                   Width="{Binding Size}" 
                   Margin="3" 
                   Height="{Binding Size}" 
                   Fill="{Binding RectangleFill}"/>
    </Grid> 
</UserControl>

As you can see, it's a black grid with a rectangle that is bound to a size and a fill property. Here's the code behind:

[Export]
public partial class SquareView : IPartImportsSatisfiedNotification 
{
    [Import]
    public RandomNumberService Service { get; set; }

    public SquareView()
    {
        InitializeComponent();
        SetValue(NameProperty,Guid.NewGuid().ToString());
        LayoutRoot.DataContext = this;            
    }

    public static readonly DependencyProperty SizeProperty =
        DependencyProperty.Register(
            "Size",
            typeof (int),
            typeof (SquareView),
            null);

    public int Size
    {
        get { return (int) GetValue(SizeProperty); }
        set { SetValue(SizeProperty, value);}
    }

    public static readonly DependencyProperty RectangleFillProperty =
        DependencyProperty.Register(
            "RectangleFill",
            typeof(SolidColorBrush),
            typeof(SquareView),
            new PropertyMetadata(new SolidColorBrush(Colors.Gray)));

    public SolidColorBrush RectangleFill
    {
        get { return (SolidColorBrush)GetValue(RectangleFillProperty); }
        set { SetValue(RectangleFillProperty, value); }
    }

    private IEnumerator<Int32> ColorWorkFlow(AsyncEnumerator ae)
    {
        int alpha = 0, red = 0, green = 0, blue = 0;

        ae.SetOperationTag("Starting random number request.");

        var randomNumberWrapper = new EventWrapper();
        randomNumberWrapper.Begin =
            () => Service.GetRandomNumber(128,
                                          result =>
                                              {
                                                  alpha = result + 128;
                                                  randomNumberWrapper.End();
                                              });
        
        var eventArgsFactory = new EventApmFactory<EventArgs>();
        EventHandler serviceEnded = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
        randomNumberWrapper.Completed += serviceEnded;
        randomNumberWrapper.Begin();

        yield return 1;
        eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

        ae.SetOperationTag("Starting request for color red.");

        randomNumberWrapper.Begin =
            () => Service.GetRandomNumber(255,
                                          result =>
                                          {
                                              red = result;
                                              randomNumberWrapper.End();
                                          });

        randomNumberWrapper.Begin();
        yield return 1;

        eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

        ae.SetOperationTag("Starting request for color green.");

        randomNumberWrapper.Begin =
            () => Service.GetRandomNumber(255,
                                          result =>
                                          {
                                              green = result;
                                              randomNumberWrapper.End();
                                          });

        randomNumberWrapper.Begin();
        yield return 1;

        eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

        ae.SetOperationTag("Starting request for color blue.");

        randomNumberWrapper.Begin =
            () => Service.GetRandomNumber(255,
                                          result =>
                                          {
                                              blue = result;
                                              randomNumberWrapper.End();
                                          });

        randomNumberWrapper.Begin();
        yield return 1;

        randomNumberWrapper.Completed -= serviceEnded;
        eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

        RectangleFill = new SolidColorBrush(new Color { A = (byte)alpha, B = (byte)blue, G = (byte)green, R = (byte)red });

        ae.SetOperationTag("End of Color Workflow.");

        yield break; 
    }
   
    public void OnImportsSatisfied()
    {
        var ae = new AsyncEnumerator("Color Workflow");
        ae.BeginExecute(ColorWorkFlow(ae), ae.EndExecute);           
    }
}

The user control itself is exported. We have to import the service for the random number, so we wait until that is available using IPartImportsSatisfiedNotification. Once it is imported, we begin our workflow. You'll notice I'm using a helper class to call to the random number service and get the alpha, red, green, and blue values for my color. When I'm done, I set it and break out of the loop. In a nutshell, here are the steps we're taking:

  1. Create an AsyncEnumerator and kick off the process
  2. Implement IEnumerator<Int32> for the workflow. The documentation for the PowerThreading library explains this. Basically, the enumerator uses the yield return statements to keep track of how many pending asynchronous calls exist. We return 1 each time we start a process because were queuing up a single call. The yield statements cause the state machine to block until the asynchronous calls are complete.

With our ICoroutine concept, we "yield" or begin the process by kicking off the actual asynchronous call. The call's end is hooked into the Async Enumerator's End method (the "yielded" concept we discussed). The yield returns are what lock the state engine to wait for the operation to complete.

If you recall the diagram above, here is what the overall process looks like:

  1. Kick it off by calling BeginExecute on the coroutine manager (our AsyncEnumerator)
  2. Wire in a process so it completes by calling the coroutine manager's End method
  3. Kick off the process
  4. Yield
  5. The engine now blocks until the asynchronous call completes and hooks back into the end method
  6. Now we dequeue the result, and decide if we wish to continue the workflow or end it

By wrapping the random service call in the event wrapper, I can use the library's EventAPMFactory to convert the event to an asynchronous result that implements IAsyncResult. You'll notice we hook into the completed event by pointing to our enumerator, which will handle grabbing the result and implementing the interface for us. When it comes back, we dequeue the result and continue on, and unhook the event handler at the end.

The code behind does some other interesting things. You'll notice it exposes the dependency properties for the size and color so it sets the data context for the grid to itself. Not always the best practice, but it made this example a little more simple. You'll also notice we aren't doing anything with the size. Where does that come from?

The size is populated by the main workflow in the main page. The main page has a text box and some story boards. It dynamically inserts the squares onto the grid surface via the workflow. Take a look at the page XAML:

<UserControl x:Class="Coroutines.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:DesignWidth="640" d:DesignHeight="480">
    <Grid>
        <Grid.Resources>
            <Storyboard x:Name="ScaleText">
                <DoubleAnimation Storyboard.TargetName="TextScale" Storyboard.TargetProperty="ScaleX"
                                 From="0.3" To="1.0" Duration="0:0:2"/>
                <DoubleAnimation Storyboard.TargetName="TextScale" Storyboard.TargetProperty="ScaleY"
                                 From="0.3" To="1.0" Duration="0:0:2"/>
            </Storyboard>
            <Storyboard x:Name="SlideText">
                <DoubleAnimation Storyboard.TargetName="TextSlide" Storyboard.TargetProperty="X"
                                 From="400" To="0" Duration="0:0:2"/>                
            </Storyboard>
            <Storyboard x:Name="HideText">
                <ObjectAnimationUsingKeyFrames 
                    Duration="0:0:2"
                    Storyboard.TargetName="Loading" Storyboard.TargetProperty="(UIElement.Visibility)">
                    <DiscreteObjectKeyFrame KeyTime="0:0:0">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Visible</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                    <DiscreteObjectKeyFrame KeyTime="0:0:2">
                        <DiscreteObjectKeyFrame.Value>
                            <Visibility>Collapsed</Visibility>
                        </DiscreteObjectKeyFrame.Value>
                    </DiscreteObjectKeyFrame>
                </ObjectAnimationUsingKeyFrames>
                <DoubleAnimation Storyboard.TargetName="Loading" Storyboard.TargetProperty="(UIElement.Opacity)"
                                 Duration="0:0:2" From="1.0" To="0"/>
            </Storyboard>
        </Grid.Resources>
        <Grid x:Name="LayoutRoot">
        
        </Grid>
        <Button x:Name="BeginButton" Content="Click to Begin" HorizontalAlignment="Center" VerticalAlignment="Center"/>
        <TextBlock FontSize="30" Foreground="White" FontWeight="Bold" x:Name="Loading" HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock.Effect>
                <DropShadowEffect/>
            </TextBlock.Effect>
            <TextBlock.RenderTransform>
                <TransformGroup>
                    <ScaleTransform x:Name="TextScale"/>
                    <TranslateTransform x:Name="TextSlide"/>
                </TransformGroup>                
            </TextBlock.RenderTransform>
        </TextBlock>
    </Grid>
</UserControl>

Notice the layout root is empty, and nested inside another grid. This allows us to layer the button to kick the workflow off and the text box we'll use to show some animations on top of everything.

The fun, of course, is in the code behind:

[Export]
public partial class MainPage : IPartImportsSatisfiedNotification
{
    [Import]
    public ExportFactory<SquareView> ViewFactory { get; set; }

    [Import]
    public RandomNumberService Service { get; set; }

    public MainPage()
    {
        InitializeComponent();            
    }
    
    public void OnImportsSatisfied()
    {
        BeginButton.Click += (o, e) =>
                                 {
                                     var asyncEnum = new AsyncEnumerator("Main Workflow");
                                     asyncEnum.BeginExecute(Workflow(asyncEnum), asyncEnum.EndExecute);
                                 };      
    }

    public IEnumerator Workflow(AsyncEnumerator ae)
    {
        BeginButton.Visibility = Visibility.Collapsed; // get rid of the button 
        
        Loading.Text = "Animating"; 

        SlideText.Completed += (o, e) => ((Storyboard)o).Stop();
        SlideText.Begin();
                    
        var eventArgsFactory = new EventApmFactory<EventArgs>();
        EventHandler storyboardEnded = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
        ScaleText.Completed += storyboardEnded;
        
        ae.SetOperationTag("Begin ScaleText Storyboard");
        ScaleText.Begin();

        yield return 1;
        ScaleText.Completed -= storyboardEnded; 
        ScaleText.Stop();
        
        eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());
        
        Loading.Text = "Loading Squares";

        int max = 400; 
        
        while (max > 5)
        {
            var routedEventWrapper = new EventWrapper();

            EventHandler eventEnded = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
            
            ae.SetOperationTag("Adding a square.");

            var square = ViewFactory.CreateExport().Value;
            square.Size = max;

            routedEventWrapper.Begin = () => LayoutRoot.Children.Add(square);
            square.Loaded += (o, e) => routedEventWrapper.End();

            routedEventWrapper.Completed += eventEnded;
            routedEventWrapper.Begin();

            yield return 1;

            routedEventWrapper.Completed -= eventEnded;                
            eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

            ae.SetOperationTag("Getting a new random number.");

            var eventWrapper = new EventWrapper();
            
            int i = max;                

            eventWrapper.Begin =
                () =>
                    {
                        Service.GetRandomNumber(i,
                                                result =>
                                                    {
                                                        max = result;
                                                        eventWrapper.End();
                                                    });
                    };

            EventHandler randomCompleted = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
            eventWrapper.Completed += randomCompleted;

            eventWrapper.Begin();

            yield return 1;

            eventWrapper.Completed -= randomCompleted;
            eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());
        }

        Loading.Text = "Goodbye";

        HideText.Begin();

        ae.SetOperationTag("End of main workflow.");
        
        yield break;
    }        
}

So the first thing you'll notice is my use of MEF's ExportFactory. This let's me get the "means to create" an export, rather than a single export value. This allows me to generate as many squares as I like, all using MEF's composition engine to inject the service so the workflow can create the random colors.

We wire the button click when the imports are ready, and that kicks off the workflow.

Notice how easy it is to read the sequential workflow. We hide the button and set the text up. I kick off one animation asynchronously (the expansion) but plug the other into the workflow. This is to show how you can mix the asynchronous and sequential calls: these are two different storyboards, but they run at the same time and only when the coroutine one ends does the text update. Our state engine allows us to have our own loops, so we continue to loop until the random value gets too small to draw a visible square. Notice I actually wait for the square to get loaded into the visual tree before I draw the next square. This means you'll get several squares with the default (gray) color visible while the color workflow fires and then eventually colors the squares when complete.

When the last square is drawn, it changes the text and fades it out and then the workflow ends.

Here is the actual application for you to play with:

There you have it! Feel free to pull down the source and kick the tires. I hope this article has done a decent job of explaining what coroutines are, how you can implement them in C# and what they are practical for. Please share your feedback and comments below as I believe this area is very interesting for discussion.

When you are ready, read Part 2: Simplified where I break down the concept further and show how to create a custom framework from scratch to simplify the use of coroutines.

Download the source for this post Note: this requires Wintellect's PowerThreading library. Download that here, then follow the instructions on the download link to add the reference.

Jeremy Likness

28 comments:

  1. This is an excellent solution to building a sequential workflow from several asynchronous calls without all of the "chaining"!!!

    Not only does it allow you keep your workflow in one centralized place, but it makes it so much easier to add new steps later as necessary. Just as I loved lambda expressions for asynchronous callbacks (because you can still work with your local variables easily, get the intellisense, etc.) the same holds true here as it is all in one neat little package.

    Thank you Jeremy, this is most awesome!

    ReplyDelete
  2. Is there source code for the power threading library?

    ReplyDelete
  3. Jeffrey Richter explains the internals in this article:

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

    ReplyDelete
  4. Hey Jeremy,

    This is a nice approach to limited coroutine usage through C# iterators --however, I have a feeling that it could be a bit more abstracted an simplified further.

    As the first commenter pointed out, all the asynchronous methods that need to occur in order for a final result to be calculated or displayed may need to 'complete' in order for a proper result.

    A definite bookmarked article for me as I've been trying to find a way to simplify this asynchronous programming where return calls are chained together. It really gets messy if you don't have a graph of the control flow and you need to insert one or more dependent asynchronous calls in the chain.

    Two thumbs up for this --two toes as well if we can simplify this example and extract it into something so simple it looks like writing a function with all sequential async calls.

    Cheers,

    JFalcon

    ReplyDelete
  5. Stay tuned I'll show you a post to conceptualize and simplify it even further shortly.

    Jeremy

    ReplyDelete
  6. "Yes, hang: the program will not end." Are you sure? Once the IntegerList reaches its natural end the Enumerators MoveNext method will return false. Doesn't hang when I tested it.

    ReplyDelete
  7. No, you make a point ... the enumerator will end when the end of the method is reached, so that part was incorrect, thanks for pointing it out! Yield break just terminates it early, without having to fall out the end of the method.

    ReplyDelete
  8. Hi Jeremy,

    Hope this post finds you all well.

    I come from a scripting background. Most of the applications I develop use XML, ASMX, Javascript, XSLT and ASP.NET.

    However, I am trying to get to the next level and would like to understand more thoroughly how delegates (also lambdas), Actions, EventArgs etc all work.

    Like you, most of the articles I have found assumed a fairly advanced base of knowledge on the above subject.

    I have read your tutorial "Sequential Asynchronous Workflows in Silverlight using Coroutines" and at the beginning you show an extract of using delegates but did not implement the code.

    I was wondering if you could kindly shed some light on how to implement the delegates when the calls come back from the WCF service.

    This would be very helpful to me.

    Cheers

    This is what you showed:


    private int n1, n2;

    private void FirstCall()
    {

    Service.GetRandomNumber(10, SecondCall);
    }


    private void SecondCall(int number)
    {

    n1 = number;

    Service.GetRandomNumber(n1, ThirdCall);
    }


    private void ThirdCall(int number)
    {

    n2 = number;


    }

    ReplyDelete
  9. I explain how to manage the delegates in this article - from the hook up to the WCF event to the contract that provides the Action delegate:
    http://csharperimage.jeremylikness.com/2009/12/simplifying-asynchronous-calls-in.html

    ReplyDelete
  10. Hi Jeremy,

    Thanks for your reply. I will have a look at the article straight away.

    By the way, I would like to make a suggestion.

    Would you consider creating a tutorial from the beginning to advanced level about creating a large Business Application with Silverlight using RIA, Caliburn, MEF etc..?

    The way you explain things are so clear and easy to grasp and a tutorial like that would be great.

    That would be bery helpful to beginner.

    Have you seen Brad Abrams tutorial on RIA services? It would be great if you could create something similar but using Caliburn and MEF so that we can learn patterns etc...

    Cheers

    Claudio

    ReplyDelete
  11. IMHO, this and all similar articles seem to bypass a simple fact that an iterator does not have its own stack.

    So, when you have multiple nested calls and/or recursion of async methods, MoveNext will return you only to the top-level yield statement. For code like this, it's no help:

    IEnumerable Process()
    {
    while (some_conditions)
    {
    ...some code

    // Async method
    BeginRead(...)

    yield xxx;

    if (Process().MoveNext())
    yield xxx;
    }
    }


    If you try to workaround that by building your own stack system (so each call in the end executes the next iterator below the stack), you will face face conditions. This is because when the callback of the async operation completes and you need to do MoveNext for the next iterator in the stack, you have no way to determine if the yield statement itself already completed.

    In other words, you start an async operation and then use yield to return to the previous level in the stack of iterators. However, the async operation may return very fast (although IAsyncResult.IsCompletedSycnhronously is still false). And your callback which does MoveNext on your iterator fails because yield is still not completed (and MoveNext got not enough data to proceed).

    This race condition is not possible to overcome. The callback has no way to know that the yield statement has completed. Even if you set ManualResetEvent.Set just before yield itself, it does not guarantee anything. You need to have it after yield. But yield already terminates the thread.

    If you use ManualResetEvent.Set in the previous iterator in the stack (after top level yield has already completed), you may first you lucky. Now the callback carefully waits until the top-level yield is finished before calling MoveNext for that iterator. But this changes nothing in the end. You still have to propagate MoveNext to all the iterators lower in the stack (otherwise, the callback will execute just the top-level method of the entire stack). And for the very end iterator you still have no way of knowing when it's done.

    ReplyDelete
  12. That's why I titled the post, "Sequential asynchronous workflows" as opposed to "Recursive Asynchronous Workflows" ... it only makes sense when you have a linear dependency on the asynchronous chain. Appreciate your detailed insights!

    ReplyDelete
  13. Phew! Another couple of restless nights and days not much different than these nights and I found the solution. BTW, I now a see a few typos in my previous post. My apologies, was way too tired and frustrated to find no reason to live on but now I'm over it :-)

    Although I would have already been satisfied if I just found a way to provide some kind of synchronization mechanism to avoid firing async callbacks before the main thread finishes propagating the entire 'yield return' stack, I stumbled upon a method which makes it possible to not use synchronization code at all.

    http://blogs.ipona.com/james/archive/2008/05/28/Dangerous-Ideas-in-C-No.-2-Yielding-to-the-Inevitable.aspx

    I'm not sure if the author ever thought of issues similar to mine (because it does not mention anything like that and there is no stack or anything like that in his code) but accidentally his approach does the trick. Yes, we cannot determine from a parallel thread (where callback is being executed) when 'yield return' has finished on the main thread. But we can fire the callback itself after 'yield return' finishes!

    So, in BeginXXX methods we must not pass any callbacks at all (because they can fire at unpredictable moment of time, maybe way too early). Instead, after completing the very first MoveNext (which in in the bottom of the stack), we must register this callback and only then bail to nowhere.

    I never thought of this because I always expected a callback is thing the function which will execute this callback should know about when being called. But with .NET approach to async stuff it's not true. A method which is supposed to execute a callback may not know anything of it (of course, it's not technically true, but you can think of it this way).

    IEnumerable SomeMethod(MyAsyncState state)
    {
    ...
    state.AsyncResult = stream.BeginRead(buf, 0, buf.Length, null, null);
    yield return 0;
    int n = stream.BeginEnd(state.AsyncResult);
    ...

    state.AsyncResult = stream.BeginRead(buf, 0, buf.Length, null, null);
    yield return 0;
    n = stream.BeginEnd(state.AsyncResult);
    ...
    }

    static void asyncProc(object state, bool timedOut)
    {
    MyAsyncState asyncState = (MyAsyncState)state;
    if (asyncState.StackOfEnumerators.Peek().MoveNext())
    {
    ThreadPool.RegisterWaitForSingleObject(asyncState.AsyncResult.AsyncWaitHandle, asyncProc, state, -1, true);
    }
    }

    It does not matter that stream.BeginRead finished ages ago to the moment when we attach a callback in asyncProc. Even if BeginRead already finished, the callback will still be executed. Most important is that we never attach the callback before MoveNext finished.

    This worked out perfectly with recursive algorithms and any other stack-based stuff I tested it with.

    And what I like even more is a total absence of ManualResetEvent.WaitOne and all other synchronization code (which is OS-based, consumes resources and better be avoid whenever possible).

    ReplyDelete
  14. Hello! This is an excellent solution to building a sequential workflow. But I think you could implement something like ActionApmFactory and ActionApmOperation classes instead of EventWrapper. So the will be no difference between using events and actions. I reflected EventApmFactory and EventApmOperation and implemented similar classes for Action and Action<T>. Here is my solution:

    [StructLayout(LayoutKind.Sequential, Size = 1), DebuggerStepThrough]
    public struct ActionApmFactory<TArg>
    {
    public ActionApmOperation<TArg> PrepareOperation(AsyncCallback callback, object state)
    {
    return new ActionApmOperation<TArg>(callback, state);
    }

    public ActionApmOperation<TArg> PrepareOperation(AsyncCallback callback)
    {
    return new ActionApmOperation<TArg>(callback, null);
    }

    public TArg EndInvoke(IAsyncResult result)
    {
    return ((AsyncResult<TArg>)result).EndInvoke();
    }

    public override bool Equals(object obj)
    {
    return (obj.GetType() == GetType());
    }

    public static bool operator ==(ActionApmFactory<TArg> factory1, ActionApmFactory<TArg> factory2)
    {
    return factory1.Equals(factory2);
    }

    public static bool operator !=(ActionApmFactory<TArg> factory1, ActionApmFactory<TArg> factory2)
    {
    return !factory1.Equals(factory2);
    }

    public override int GetHashCode()
    {
    return base.GetHashCode();
    }
    }

    [DebuggerStepThrough]
    public sealed class ActionApmOperation<TArg>
    {
    private readonly AsyncCallback _callback;
    private readonly object _state;

    internal ActionApmOperation(AsyncCallback callback, object state)
    {
    _callback = callback;
    _state = state;
    }

    public void Action(TArg arg)
    {
    var result = new AsyncResult<TArg>(_callback, _state);
    result.SetAsCompleted(arg, false);
    }
    }

    Usage example:
    var downloadCallbackFactory = new ActionApmFactory();
    var op = downloadCallbackFactory.PrepareOperation(ae.End());
    CatalogService.DownloadAsync("MyModule.xap", op.Action);
    yield return 1;
    var isOk = downloadCallbackFactory.EndInvoke(ae.DequeueAsyncResult());

    It's easy to modify this sample to target Action without type arguments.

    ReplyDelete
  15. Very nice! Thank you for sharing this!

    ReplyDelete
  16. It seems like this would also work using the Rx extensions. I've posted an example of doing this at:
    http://secondstanza.com/2010/06/19/sequential-asynchronous-workflows-in-silverlight-using-rx-framework/

    It doesn't really address the benefits of coroutines, but it does show that the Rx extensions will work as well for sequential workflows.

    ReplyDelete
  17. I understand the new concept and it is exactly what I wanted but the actual example loses me. I'm sorry for not being quick enough but this example is a little complicated for me just learning. So for a simple example lets just work with 2 WCF calls:

    List asdf = WebService.Method1();
    WebService.Method2(asdf);

    Now how do I get these two methods hooked to the state machine?

    ReplyDelete
  18. No worries ... let me ask this first: have you read part 2 yet? That is a far more simplified examle, in my opinion. Take a look there, if you've already read it or still don't get it, come back and we'll put together the example you asked for.

    Part 2:
    http://csharperimage.jeremylikness.com/2010/03/sequential-asynchronous-workflows-part.html

    ReplyDelete
  19. Well I was rather trepedatious about reading part 2 thinking it would be more complicated then part one. Standby....

    ReplyDelete
  20. OK After reading part 2 I am trying to work my way through this and not bother you but this line is confusing me.

    SlideText.Completed += (o, e) => ((Storyboard)o).Stop();

    It is some sort of Lamda expression for event handling?

    ReplyDelete
  21. I am also unsure of how to handle events. It appears so far that my sequential flow is going properly. When the web service returns however we, as you said in the article, return right where we left the method at...but where are and how do I handle those return event args as I can't find them anywhere in my watches. The wcf agrs contain the data (in this case in the form of List) that I need to access?

    ReplyDelete
  22. At this point you are almost better off looking into a framework like Reactive Extensions (Rx) - much cleaner way to explicitly hook into and compose events. Check out:

    http://www.silverlightshow.net/items/Using-Reactive-Extensions-in-Silverlight.aspx

    The section "turning events into observables."

    ReplyDelete
  23. Yes, I had seen this and bookmarked for Sandbox time later but unfortunately I am in /NET 3.5 and SL 3x. I'm so close to doing this without chaining together methods if I could just get at those event args or modify the web service somehow as I wrote that as well.

    ReplyDelete
  24. OK Now check this out. Why not register my event handler as well is how my brain tries to get around this problems so I do this and it works! Any major flaws to this solution you can see?

    private IEnumerator GetYearlyData(AsyncEnumerator ae)
    {


    var eventArgsFactory = new EventApmFactory();
    EventHandler evnt_LoanTypes = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
    EdWebService.GetEdDbaseLoanTypesCompleted += evnt_LoanTypes;

    EdWebService.GetEdDbaseLoanTypesCompleted += new EventHandler(EdWebService_GetEdDbaseLoanTypesCompleted);

    ae.SetOperationTag("Debugging flag but when / where?");
    EdWebService.GetEdDbaseLoanTypesAsync(7);


    yield return 1;

    EdWebService.GetEdDbaseLoanTypesCompleted -= evnt_LoanTypes;
    eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());


    EdWebService.GetAllLoansCountValueCompleted += new EventHandler(EdWebService_GetAllLoansCountValueCompleted);
    EdWebService.GetAllLoansCountValueAsync(7, DateTime.Parse("07-01-2010"), DateTime.Parse("07-31-2010"));

    yield return 1;

    //More to come

    yield break;

    //Pay attention. First we need to get all loan types possible. Once that's done we can call
    //subsequent web service calls. If we already have the loan types in memory we don't take the time to get them again
    //GetLoanTypes();
    //yield return 1;

    }

    Yes there are hard codes in there but they will be replaced with return data from service calls.

    ReplyDelete
  25. I have had two Silverlight projects, and in both sequencing async calls got very complicated quickly. I found this post and thought I would re-factor some existing code.
    However I have hit on the problem that my effective logic is:

    foreach()
    {
    Some async calls
    while()
    Some async calls
    while() some async calls
    }

    unfortunately, on the second loop through the foreach the while loops lose there mojo, and the yield return 1 statements get passed over.

    so while loops stuff it all up, which makes me sad ;(
    - I have to go back to an extremely complex set of callbacks

    ReplyDelete
  26. Looks like just what I needed with some very complex chained async calls I have to make. However I have found that it seems to be broken when it comes to loops. In the following code snippet on the second loop, on the second time through the event gets fired twice for the one call, and then the third time through it drops though the yield (I assume because it was called twice in the previous loop!)

    #region Setup EventWrapper & Factory for Async operations

    EventWrapper processFile = new EventWrapper();
    var eventArgsFactory = new EventApmFactory();
    EventHandler serviceEnded = eventArgsFactory.PrepareOperation(ae.End()).EventHandler;
    processFile.Completed += serviceEnded;

    #endregion

    uxProgressText.Text = "Initializing";

    //sequentually go through each file
    foreach (var file in _fileList)
    {
    PlaygroupExcelData data = new PlaygroupExcelData();
    if (!data.OpenFile(file))
    continue; //move to next file

    for (int worksheetNo = 4; worksheetNo < data.Workbook.Worksheets.Count - 2; worksheetNo++)
    {
    #region Check to see if worksheet is loaded
    ae.SetOperationTag("Check to see if worksheet is loaded");
    uxProgressText.Text = "Check to see if worksheet " + data.Workbook.Worksheets[worksheetNo].Name + " has already been loaded";

    int existsResult = -1;
    ExistsWrapper x = new ExistsWrapper(_proxy);
    processFile.Begin = () => x.Exists(data.Key(worksheetNo), result =>
    {
    existsResult = result;
    processFile.End();
    });

    ReplyDelete
  27. Ahh, think I worked out the issue. Basically each time you go through the loop you are adding another event to the operation, and thus the IEnumerator gets out of sync. So to make your process loop safe you have to do something like the following:

    #region Async operation to Delete current workbook

    ae.SetOperationTag("Deleting month");
    uxProgressText.Text = ("Deleting existing data loaded for " + data.Workbook.Worksheets[worksheetNo].Name);

    holdDeleteCompleted = delegate(object o, System.ComponentModel.AsyncCompletedEventArgs e)
    {
    processFile.End();
    };
    processFile.Begin = () =>
    {
    _proxy.DeleteCompleted += holdDeleteCompleted;
    _proxy.DeleteAsync(AllMetaData.AppName, 4, existsResult);
    };

    processFile.Begin();
    yield return 1;
    eventArgsFactory.EndInvoke(ae.DequeueAsyncResult());

    //remove anonymous delegate
    _proxy.DeleteCompleted -= holdDeleteCompleted;

    #endregion

    ReplyDelete