Category Archives: Eduasync

Eduasync part 13: first look at coroutines with async

(This part covers project 18 in the source code.)

As I mentioned in earlier parts, the "awaiting" part of async methods is in no way limited to tasks. So long as we have a suitable GetAwaiter() method which returns a value of a type which in turn has suitable methods on it, the compiler doesn’t really care what’s going on. It’s time to exploit that to implement some form of coroutines in C#.

Introduction to coroutines

The fundamental idea of coroutines is to have multiple methods executing cooperatively, each of them maintaining their position within the coroutine when they yield to another. You can almost think of them as executing in multiple threads, with only one thread actually running at a time, and signalling between the different threads to control flow. However, we don’t really need multiple threads once we’ve got continuations – we can have a single thread with a complex flow of continuations, and still only a very short "real" stack. (The control flow is stored in normal collections instead of being implicit on the thread’s stack.)

Coroutines were already feasible in C# through the use of iterator blocks, but the async feature of C# allows a slightly more natural way of expressing them, in my view. (The linked Wikipedia page gives a sketch of how coroutines can be built on top of generators, which in the general concept that iterator blocks implement in C#.)

I have implemented various flavours of coroutines in Eduasync. It’s possible that some (all?) of them shouldn’t strictly be called coroutines, but they’re close enough to the real thing in feeling. This is far from an exhaustive set of approaches. Once you’ve got the basic idea of what I’m doing, you may well want to experiment with your own implementations.

I’m not going to claim that the use of coroutines in any of my examples really makes any sense in terms of making real tasks easier. This is purely for the sake of interest and twisting the async feature for fun.

Round-robin independent coroutines

Our first implementation of coroutines is relatively simple. A coordinator effectively "schedules" the coroutines it’s set up with in a round-robin fashion: when one of the coroutines yields control to the coordinator, the coordinator remembers where the coroutine had got to, and then starts the next one. When each coroutine has executed its first piece of code and yielded control, the coordinator will go back to the first coroutine to continue execution, and so on until all coroutines have completed.

The coroutines don’t know about each other, and no data is being passed between them.

Hopefully it’s reasonably obvious that the coordinator contains all the smarts here – the coroutines themselves can be relatively dumb. Let’s look at what the client code looks like (along with the results) before we get to the coordinator code.

Client code

The sample code contains three coroutines, all of which take a Coordinator parameter and have a void return type. These are passed to a new coordinator using a collection initializer and method group conversions; the coordinator is then started. Here’s the entry point code for this:

private static void Main(string[] args)
{
    var coordinator = new Coordinator { 
        FirstCoroutine,
        SecondCoroutine,
        ThirdCoroutine
    };
    coordinator.Start();
}

When each coroutine is initially started, the coordinator passes a reference to itself as the argument to the coroutine. That’s how we solve the chicken-and-egg problem of the coroutine and coordinator having to know about each other. The way a coroutine yields control is simply by awaiting the coordinator. The result type of this await expression is void – it’s just a way of "pausing" the coroutine.

We’re not doing anything interesting in the actual coroutines – just tracing the execution flow. Of course we could do anything we wanted, within reason. We could even await a genuinely asynchronous task such as fetching a web page asynchronously. In that case the whole coroutine collection would be "paused" until the fetch returned.

Here’s the code for the first coroutine – the second and third ones are similar, but use different indentation for clarity. The third coroutine is also shorter, just for fun – it only awaits the coordinator once.

private static async void FirstCoroutine(Coordinator coordinator)
{
    Console.WriteLine("Starting FirstCoroutine");
    Console.WriteLine("Yielding from FirstCoroutine…");

    await coordinator;

    Console.WriteLine("Returned to FirstCoroutine");
    Console.WriteLine("Yielding from FirstCoroutine again…");

    await coordinator;

    Console.WriteLine("Returned to FirstCoroutine again");
    Console.WriteLine("Finished FirstCoroutine");
}

And here’s the output…

Starting FirstCoroutine
Yielding from FirstCoroutine…
    Starting SecondCoroutine
    Yielding from SecondCoroutine…
        Starting ThirdCoroutine
        Yielding from ThirdCoroutine…
Returned to FirstCoroutine
Yielding from FirstCoroutine again…
    Returned to SecondCoroutine
    Yielding from SecondCoroutine again…
        Returned to ThirdCoroutine
        Finished ThirdCoroutine…
Returned to FirstCoroutine again
Finished FirstCoroutine
    Returned to SecondCoroutine again
    Finished SecondCoroutine

Hopefully that’s the output you expected, given the earlier description. Again it may help if you think of the coroutines as running in separate pseudo-threads: the execution within each pseudo-thread is just linear, and the timing is controlled by our explicit "await" expressions. All of this would actually be pretty easy to implement using multiple threads which really did just block on each await expression – but the fun part is keeping it all in one real thread. Let’s have a look at the coordinator.

The Coordinator class

Some of the later coroutine examples end up being slightly brainbusting, at least for me. This one is relatively straightforward though, once you’ve got the basic idea. All we need is a queue of actions to execute. After initialization, we want our queue to contain the coroutine starting points.

When a coroutine yields control, we just need to add the remainder of it to the end of the queue, and move on to the next item. Obviously the async infrastructure will provide "the remainder of the coroutine" as a continuation via the OnContinue method.

When a coroutine just returns, we continue with the next item in the queue as before – it’s just that we won’t add a continuation to the end of the queue. Eventually (well, hopefully) we’ll end up with an empty queue, at which point we can stop.

Initialization and a choice of data structures

We’ll represent our queue using Queue<T> where the T is a delegate type. We have two choices here though, because we have two kinds of delegate – one which takes the Coordinator as a parameter (for the initial coroutine setup) and one which has no parameters (for the continuations). Fortunately we can convert between the two in either direction very simply, bearing in mind that all of this is within the context of a coordinator. For example:

// If we’re given a coroutine and want a plain Action
Action<Coordinator> coroutine = …; 
Action action = () => coroutine(this);

// If we’re given a plain Action and want an Action<Continuation>:
Action continuation = …; 
Action<Coordinator> coroutine = ignored => continuation();

I’ve arbitrarily chosen to use the first option, so there’s a Queue<Action> internally.

Now we need to get the collection initializer working. The C# compiler requires an appropriate Add method (which is easy) and also checks that the type implements IEnumerable. We don’t really need to be able to iterate over the queue of actions, so I’ve use explicit interface implementation to reduce the likelihood of GetEnumerator() being called inappropriately, and made the method throw an exception for good measure. That gives us the skeleton of the class required for setting up:

public sealed class Coordinator : IEnumerable
{
    private readonly Queue<Action> actions = new Queue<Action>();

    // Used by collection initializer to specify the coroutines to run
    public void Add(Action<Coordinator> coroutine)
    {
        actions.Enqueue(() => coroutine(this));
    }

    // Required for collection initializers, but we don’t really want
    // to expose anything.
    IEnumerator IEnumerable.GetEnumerator()
    {
        throw new NotSupportedException("IEnumerable only supported to enable collection initializers");
    }
}

(Note that I haven’t used XML documentation anywhere here – it’s great for real code, but adds clutter in blog posts.)

For production code I’d probably prevent Add from being called after the coordinator had been started, but there’s no need to do it in our well-behaved sample code. We’re only going to add extra actions to the queue via continuations, which will be added due to await expressions.

The main execution loop and async infrastructure

So far we’ve got code to register coroutines in the queue – so now we need to execute them. Bearing in mind that the actions themselves will be responsible for adding continuations, the main loop of the coordinator is embarrassingly simple:

// Execute actions in the queue until it’s empty. Actions add *more*
// actions (continuations) to the queue by awaiting this coordinator.
public void Start()
{
    while (actions.Count > 0)
    {
        actions.Dequeue().Invoke();
    }
}

Of course, the interesting bit is the code which supports the async methods and await expressions. We know we need to provide a GetAwaiter() method, but what should that return? Well, we’re just going to use the awaiter to add a continuation to the coordinator’s queue. It’s got no other state than that – so we might as well return the coordinator itself, and put the other infrastructure methods directly in the coordinator.

Again, this is slightly ugly, as the extra methods don’t really make sense on the coordinator – we wouldn’t want to call them directly from client code, for example. However, they’re fairly irrelevant – we could always create a nested type which just had a reference to its "parent" coordinator if we wanted to. For simplicity, I haven’t bothered with this – I’ve just implemented GetAwaiter() trivially:

// Used by await expressions to get an awaiter
public Coordinator GetAwaiter()
{
    return this;
}

So, that leaves just three members still to implement: IsCompleted, OnCompleted and GetResult. We always want the IsCompleted property to return false, as otherwise the coroutine will just continue executing immediately without returning to cede control; the await expression would be pointless. OnCompleted just needs to add the continuation to the end of the queue – we don’t need to attach it to a task, or anything like that. Finally, GetResult is a no-op – we have no results, no exceptions, and basically nothing to do. You might want to add a bit of logging here, if you were so inclined, but there’s no real need.

So, here are the final three members of Coordinator:

// Force await to yield control
public bool IsCompleted { get { return false; } }

public void OnCompleted(Action continuation)
{
    // Put the continuation at the end of the queue, ready to
    // execute when the other coroutines have had a go.
    actions.Enqueue(continuation);
}

public void GetResult()
{
    // Our await expressions are void, and we never need to throw
    // an exception, so this is a no-op.
}

And that’s it! Fewer than 50 lines of code required, and nothing complicated at all. The interesting behaviour is all due to the way the C# compiler uses the coordinator when awaiting it.

We need AsyncVoidMethodBuilder as before, as we have some async void methods – but that doesn’t need to do anything significant. That’s basically all the code required to implement these basic round-robin coroutines.

Conclusion

Our first foray into the weird and wonderful world of coroutines was relatively tame. The basic idea of a coordinator keeping track of the state of all the different coroutines in one sense or another will keep coming back to us, but with different ways of controlling the execution flow.

Next time we’ll see some coroutines which can pass data to each other.

Eduasync part 12: Observing all exceptions

(This post covers projects 16 and 17 in the source code.)

Last time we looked at unwrapping an AggregateException when we await a result. While there are potentially other interesting things we could look at with respect to exceptions (particularly around cancellation) I’m just going to touch on one extra twist that the async CTP implements before I move on to some weird ways of using async.

TPL and unobserved exceptions

The Task Parallel Library (TPL) on which the async support is based has some interesting behaviour around exceptions. Just as it’s entirely possible for more than one thing to go wrong with a particular task, it’s also quite easy to miss some errors, if you’re not careful.

Here’s a simple example of an async method in C# 5 where we create two tasks, both of which will throw exceptions:

private static async Task<int> CauseTwoFailures()
{
    Task<int> firstTask = Task<int>.Factory.StartNew(() => {
        throw new InvalidOperationException();
    });
    Task<int> secondTask = Task<int>.Factory.StartNew(() => {
        throw new InvalidOperationException();
    });

    int firstValue = await firstTask;
    int secondValue = await secondTask;

    return firstValue + secondValue;
}

Now the timing of the two tasks is actually irrelevant here. The first task will always throw an exception, which means we’re never going to await the second task. That means there’s never any code which asks for the second task’s result, or adds a continuation to it. It’s alone and unloved in a cruel world, with no-one to observe the exception it throws.

If we call this method from the Eduasync code we’ve got at the moment, and wait for long enough (I’ve got a call to GC.WaitForPendingFinalizers in the same code) the program will abort, with this error:

Unhandled Exception: System.AggregateException: A Task’s exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. —> System.InvalidOperationException: Operation is not valid due to the current state of the object.

Ouch. The TPL takes a hard line on unobserved exceptions. They indicate failures (presumably) which you’ll never find out about until you start caring about the result of a task. Basically there are various ways of "observing" a task’s failure, whether by performing some act which causes it to be thrown (usually as part of an AggregateException) or just asking for the exception for a task which is known to be faulted. An unobserved exception will throw an InvalidOperationException in its finalizer, usually causing the process to exit.

That works well in "normal" TPL code, where you’re explicitly managing tasks – but it’s not so handy in async, where perfectly reasonable looking code which starts a few tasks and then awaits them one at a time (possibly doing some processing in between) might hide an unobserved exception.

Observing all exceptions

Fortunately TPL provides a way of us to get out of the normal task behaviour. There’s an event TaskScheduler.UnobservedTaskException which is fired by the finalizer before it goes bang. The handlers of the event are allowed to observe the exception using UnobservedTaskExceptionEventArgs.SetObserved and can also check whether it’s already been observed.

So all we have to do is add a handler for the event and our program doesn’t crash any more:

TaskScheduler.UnobservedTaskException += (sender, e) =>
{
    Console.WriteLine("Saving the day! This exception would have been unobserved: {0}",
                      e.Exception);
    e.SetObserved();
};

In Eduasync this is currently only performed explicitly, in project 17. In the async CTP something like this is performed as part of the type initializer for AsyncTaskMethodBuilder<T>, which you can unfortunately tell because that type initializer crashes when running under medium trust. (That issue will be fixed before the final release.)

Global changes

This approach has a very significant effect: it changes the global behaviour of the system. If you have a system which uses the TPL and you want the existing .NET 4 behaviour of the process terminating when you have unobserved exceptions, you basically can’t use async at all – and if you use any code which does, you’ll see the more permissive behaviour.

You could potentially add your own event handler which aborted the application forcibly, but that’s not terribly nice either. You should quite possibly add a handler to at least log these exceptions, so you can find out what’s been going wrong that you haven’t noticed.

Of course, this only affects unobserved exceptions – anything you’re already observing will not be affected. Still, it’s a pretty big change. I wouldn’t be surprised if this aspect of the behaviour of async in C# 5 changed before release; it feels to me like it isn’t quite right yet. Admittedly I’m not sure how I would suggest changing it, but effectively reversing the existing behaviour goes against Microsoft’s past behaviour when it comes to backwards compatibility. Watch this space.

Conclusion

It’s worth pondering this whole issue yourself (and the one from last time), and making your feelings known to the team. I think it’s symptomatic of a wider problem in software engineering: we’re not really very good at handling errors. Java’s approach of checked exceptions didn’t turn out too well in my view, but the "anything goes" approach of C# has problems too… and introducing alternative models like the one in TPL makes things even more complicated. I don’t have any smart answers here – just that it’s something I’d like wiser people than myself to think about further.

Next, we’re going to move a bit further away from the "normal" ways of using async, into the realm of coroutines. This series is going to get increasingly obscure and silly, all in the name of really getting a feeling for the underlying execution model of async, before it possibly returns to more sensible topics such as task composition.

Eduasync part 11: More sophisticated (but lossy) exception handling

(This post covers projects 13-15 in the source code.)

Long-time readers of this blog may not learn much from this post – it’s mostly going over what I’ve covered before. Still, it’s new to Eduasync.

Why isn’t my exception being caught properly?

Exceptions are inherently problematic in C# 5. There are two conflicting aspects:

  • The point of the async feature in C# 5 is that you can write code which mostly looks like its synchronous code. We expect to be able to catch specific exception types as normal.
  • Asynchronous code may potentially have multiple exceptions "at the same time". The language simply isn’t designed to deal with that in the synchronous case.

Now if the language had been designed for asynchrony to start with, perhaps exception flow would have been designed differently – but we are where we are, and we all expect exceptions to work in a certain way.

Let’s make all of this concrete with a sample:

private static void Main(string[] args)
{
    Task<int> task = FetchOrDefaultAsync();
    Console.WriteLine("Result: {0}", task.Result);
}

private static async Task<int> FetchOrDefaultAsync()
{
    // Nothing special about IOException here
    try
    {
        Task<int> fetcher = Task<int>.Factory.StartNew(() => { throw new IOException(); });
        return await fetcher;
    }
    catch (IOException e)
    {
        Console.WriteLine("Caught IOException: {0}", e);
        return 5;
    }
    catch (Exception e)
    {
        Console.WriteLine("Caught arbitrary exception: {0}", e);
        return 10;
    }
}

Here we have a task which will throw an IOException, and some code which awaits that task – and has a catch block for IOException.

So, what would you expect this to print? With the code we’ve got in Eduasync so far, we get this:

Caught arbitrary exception: System.AggregateException: One or more errors occurred. —> System.IO.IOException: I/O error occurred.

Result: 10

If you run the same code against the async CTP, you get this:

Caught IOException: System.IO.IOException: I/O error occurred.

Result: 5

Hmm… we’re not behaving as per the CTP, and we’re not behaving as we’d really expect the normal synchronous code to behave.

The first thing to work out is which boundary we should be fixing. In this case, the problem is between the async method and the task we’re awaiting, so the code we need to fix is TaskAwaiter<T>.

Handling AggregateException in TaskAwaiter<T>

Before we can fix it, we need to work out what’s going on. The stack trace I hid before actually show this reasonably clearly, with this section:

at System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceled Exceptions)
at System.Threading.Tasks.Task`1.get_Result()
at Eduasync.TaskAwaiter`1.GetResult() …TaskAwaiter.cs:line 40
at Eduasync.Program.<FetchOrDefaultAsync>d__2.MoveNext() …Program.cs:line 37

So AggregateException is being thrown by Task<T>.Result, which we’re calling from TaskAwaiter<T>.GetResult(). The documentation for Task<T>.Result isn’t actually terribly revealing here, but the fact that Task<T>.Exception is of type AggregateException is fairly revealing.

Basically, the Task Parallel Library is built with the idea of multiple exceptions in mind – whereas our async method isn’t.

Now the team in Microsoft could have decided that really you should catch AggregateException and iterate over all the exceptions contained inside the exception, handling each of them separately. However, in most cases that isn’t really practical – because in most cases there will only be one exception (if any) and all that looping is relatively painful. They decided to simply extract the first exception from the AggregateException within a task, and throw that instead.

We can do that ourselves in TaskAwaiter<T>, like this:

try
{
    return task.Result;
}
catch (AggregateException aggregate)
{
    if (aggregate.InnerExceptions.Count > 0)
    {
        // Loses the proper stack trace. Oops. For workarounds, see
        // See http://bradwilson.typepad.com/blog/2008/04/small-decisions.html
        throw aggregate.InnerExceptions[0];
    }
    else
    {
        // Nothing better to do, really…
        throw;
    }
}

As you can tell from the comment, we end up losing the stack trace using this code. I don’t know exactly how the stack trace is preserved in the real Async CTP, but it is. I suspect this is done in a relatively obscure way at the moment – it’s possible that for .NET 5, there’ll be a cleaner way that all code can take advantage of.

This code is also pretty ugly, catching the exception only to rethrow it. We can check whether or not the task has faulted using Task<T>.Status and extract the AggregateException using Task<T>.Exception instead of forcing it to be thrown and catching it. We’ll see an example of that in a minute.

With our new code in place, we can catch the IOException in our async code very easily.

What if I want all the exceptions?

In certain circumstances it really makes sense to collect multiple exceptions. This is particularly true when you’re waiting for multiple tasks to complete, e.g. with TaskEx.WhenAll in the Async CTP. This caused me a certain amount of concern for a while, but when Mads came to visit in late 2010 and we talked it over, we realized we could use the compositional nature of Task<T> and the convenience of TaskCompletionSource to implement an extension method preserving all the exceptions.

As we’ve seen, when a task is awaited, its AggregateException is unwrapped, and the first exception rethrown. So if we create a new task which adds an extra layer of wrapping and make our code await that instead, only the extra layer will be unwrapped by the task awaiter, leaving the original AggregateException. To come up with a new task which "looks like" an existing task, we can simply create a TaskCompletionSource, add a continuation to the original task, and return the completion source’s task as the wrapper. When the continuation fires we’ll set the appropriate result on the completion source – cancellation, an exception, or the successful result.

You may expect that we’d have to create a new AggregateException ourselves – but TaskCompletionSource.SetException will already do this for us. This makes it looks like the code below isn’t performing any wrapping at all, but remember that Task<T>.Exception is already an AggregateException, and calling TaskCompletionSource.SetException will wrap it in another AggregateException. Here’s the extension method in question:

public static Task<T> WithAllExceptions<T>(this Task<T> task)
{
    TaskCompletionSource<T> tcs = new TaskCompletionSource<T>();

    task.ContinueWith(ignored =>
    {
        switch (task.Status)
        {
            case TaskStatus.Canceled:
                tcs.SetCanceled();
                break;
            case TaskStatus.RanToCompletion:
                tcs.SetResult(task.Result);
                break;
            case TaskStatus.Faulted:
                // SetException will automatically wrap the original AggregateException
                // in another one. The new wrapper will be removed in TaskAwaiter, leaving
                // the original intact.
                tcs.SetException(task.Exception);
                break;
            default:
                tcs.SetException(new InvalidOperationException("Continuation called illegally."));
                break;
        }
    });

    return tcs.Task;
}

Here you can see the cleaner way of reacting to a task’s status – we don’t just try to fetch the result and catch any exceptions; we handle each status individually.

I don’t know offhand what task scheduler is used for this continuation – it may be that we’d really want to specify the current task scheduler for a production-ready version of this code. However, the core idea is sound.

It’s easy to use this extension method within an async method, as shown here:

private static async Task<int> AwaitMultipleFailures()
{
    try
    {
        await CauseMultipleFailures().WithAllExceptions();
    }
    catch (AggregateException e)
    {
        Console.WriteLine("Caught arbitrary exception: {0}", e);
        return e.InnerExceptions.Count;
    }
    // Nothing went wrong, remarkably!
    return 0;
}

private static Task<int> CauseMultipleFailures()
{
    // Simplest way of inducing multiple exceptions
    Exception[] exceptions = { new IOException(), new ArgumentException() };
    TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
    tcs.SetException(exceptions);
    return tcs.Task;
}

Note that this will work perfectly well with the Async CTP and should be fine with the full release as well. I wouldn’t be entirely surprised to find something similar provided by the framework itself by release time, too.

Conclusion

It’s worth being aware of the impedance mismatch between the TPL and async methods in C# 5, as well as how this mismatch is handled. I dislike the idea of data loss, but I can see why it’s being handled in this way. It’s very much in line with the approach of trying to make asynchronous methods look like synchronous ones as far as possible.

We’ll probably look at the compositional nature of tasks again later in the series, but this was one simple example of how transparent it can be – a simple extension method can change the behaviour to avoid the risk of losing exception information when you’re expecting that multiple things can go wrong.

It’s worth remembering that this behaviour is very specific to Task and Task<T>, and the awaiter types associated with them. If you’re awaiting other types of expressions, they may behave differently with respect to exceptions.

Before we leave the topic of exceptions, there’s one other aspect we need to look at – what happens when an exception isn’t observed

Eduasync part 10: CTP bug – don’t combine multiple awaits in one statement…

(This post covers project 12 in the source code.)

Last time, we saw what happens when we have multiple await expressions: we end up with multiple potential states in our state machine. That’s all fine, but there’s a known bug in the current CTP which affects the code generated when you have multiple awaits in the same statement. (Lucian Wischik calls these "nested awaits" although I personally don’t really think of one as being nested inside an another.)

This post is mostly a cautionary tale for those using the CTP and deploying it to production – but it’s also interesting to see the kind of thing that can go wrong. I’m sure this will all be fixed well before release, and the team are already very aware of it. (I expect it’s been fixed internally for a while. It’s not the kind of bug you’d hold off fixing.)

Just as a reminder, here’s the code we used last time to demonstrate multiple await expressions:

// Code from part 9
private static async Task<int> Sum3ValuesAsyncWithAssistance()
{
    Task<int> task1 = Task.Factory.StartNew(() => 1);
    Task<int> task2 = Task.Factory.StartNew(() => 2);
    Task<int> task3 = Task.Factory.StartNew(() => 3);

    int value1 = await task1;
    int value2 = await task2;
    int value3 = await task3;

    return value1 + value2 + value3;
}

To simplify things a bit, let’s reduce it to two tasks. Then as a refactoring, I’m going to perform the awaiting within the summation expression:

private static async Task<int> Sum2ValuesAsyncWithAssistance()
{
    Task<int> task1 = Task.Factory.StartNew(() => 1);
    Task<int> task2 = Task.Factory.StartNew(() => 2); 

    return await task1 + await task2;
}

So, with the local variables value1 and value2 gone, we might expect that in the generated state machine, we’d have lost the corresponding fields. But should we, really?

Think what happens if task1 has completed (and we’ve fetched the results), but task2 hasn’t completed by the time we await it. So we call OnContinue and exit as normal, then keep going when the continuation is invoked.

At that point we should be ready to return… but we’ve got to use the value returned from task1. We need the result to be stored somewhere, and fields are basically all we’ve got.

Unfortunately, in the current async CTP we don’t have any of these – we just have two local awaiter result variables. Here’s the decompiled code, stripped of the outer skeleton as normal:

  int awaitResult1 = 0;
  int awaitResult2 = 0;
  switch (state)
  {
      case 1:
          break;

      case 2:
          goto Label_Awaiter2Continuation;

      default:
          if (state != -1)
          {
              task1 = Task.Factory.StartNew(() => 1);
              task2 = Task.Factory.StartNew(() => 2);

              awaiter1 = task1.GetAwaiter();
              if (awaiter1.IsCompleted)
              {
                  goto Label_GetAwaiter1Result;
              }
              state = 1;
              doFinallyBodies = false;
              awaiter1.OnCompleted(moveNextDelegate);
          }
          return;
  }
  state = 0;
Label_GetAwaiter1Result:
  awaitResult1 = awaiter1.GetResult();
  awaiter1 = default(TaskAwaiter<int>());

  awaiter2 = task2.GetAwaiter();
  if (awaiter2.IsCompleted)
  {
      goto Label_GetAwaiter2Result;
  }
  state = 2;
  doFinallyBodies = false;
  awaiter2.OnCompleted(moveNextDelegate);
  return;
Label_Awaiter2Continuation:
  state = 0;
Label_GetAwaiter2Result:
  awaitResult2 = awaiter2.GetResult();
  awaiter2 = default(TaskAwaiter<int>());

  result = awaitResult1 + awaitResult2;

The first half of the code looks like last time (with the natural adjustments for only having two tasks) – but look at what happens when we’ve fetched the result of task1. We fetch the result into awaitResult1, which is a local variable. We then don’t touch awaitResult1 again unless we reach the end of the code – which will not happen if awaiter2.IsCompleted returns false. When the method returns, the local variable’s value is lost forever… although it will be reset to 0 next time we re-enter, and nothing in the generated code will detect a problem.

So depending on the timing of the two tasks, the final result can be 3 (if the second task has finished by the time it’s checked), or 2 (if we need to add a continuation to task2 instead). This is easy to verify by forcing the tasks to sleep before they return.

Conclusion

The moral of this post is threefold:

  • In general, treat CTP and beta-quality code appropriately: I happened to run into this bug, but I’m sure there are others. This is in no way a criticism of the team. It’s just a natural part of developing a complex feature.
  • To avoid this specific bug, all you have to do is make sure that all relevant state is safely stored in local variables before any logical occurrence of "await".
  • If you’re ever writing a code generator which needs to store state, remember that that state can include expressions which have only been half evaluated so far.

For the moment, this is all I’ve got on the generated code. Next time, we’ll start looking at how exceptions are handled, and in particular how the CTP behaviour differs from the implementation we’ve seen so far. After a few posts on exceptions, I’ll start covering some daft and evil things I’ve done with async.

Eduasync part 9: generated code for multiple awaits

Last time we looked at a complex async method with nested loops and a single await. This post is the exact opposite – the method is going to look simple, but it will have three await expressions in. If you’re glancing down this post and feel put off by the amount of code, don’t worry – once you’ve got the hang of the pattern, it’s really pretty simple.

Here’s the async method we’re going to analyze:

private static async Task<int> Sum3ValuesAsyncWithAssistance()
{
    Task<int> task1 = Task.Factory.StartNew(() => 1);
    Task<int> task2 = Task.Factory.StartNew(() => 2);
    Task<int> task3 = Task.Factory.StartNew(() => 3);

    int value1 = await task1;
    int value2 = await task2;
    int value3 = await task3;

    return value1 + value2 + value3;
}

Nice and straightforward: start three tasks, then sum their results. Before we look at the decompiled code, it’s worth noting that writing it this way allows the three (admittedly trivial tasks) to run in parallel. If we’d written it this way instead:

private static async Task<int> Sum3ValuesAsyncWithAssistance()
{
    // No parallelism…
    int value1 = await Task.Factory.StartNew(() => 1);
    int value2 = await Task.Factory.StartNew(() => 2);
    int value3 = await Task.Factory.StartNew(() => 3);

    return value1 + value2 + value3;
}

… then we’d have waited for the first task to finish before starting the second one, then waited for the second one to complete before we started the third one. That’s appropriate when there are dependencies between your tasks (i.e. you need the result of the first as an input to the second) and it would still have been asynchronous but when you can start multiple independent tasks together, that’s generally what you want to do. Don’t forget that this doesn’t just extend to CPU-bound tasks – you might want to launch tasks making multiple web service calls in parallel, before collecting the results.

As before, I’ll just show the heart of the generated code, without its boiler-plate skeleton. Again, the full code is available online.

Generated code

  switch (state)
  {
      case 1:
          break;

      case 2:
          goto Label_Awaiter2Continuation;

      case 3:
          goto Label_Awaiter3Continuation;

      default:
          if (state != -1)
          {
              task1 = Task.Factory.StartNew(() => 1);
              task2 = Task.Factory.StartNew(() => 2);
              task3 = Task.Factory.StartNew(() => 3);

              awaiter1 = task1.GetAwaiter();
              if (awaiter1.IsCompleted)
              {
                  goto Label_GetAwaiter1Result;
              }
              state = 1;
              doFinallyBodies = false;
              awaiter1.OnCompleted(moveNextDelegate);
          }
          return;
  }
  state = 0;
Label_GetAwaiter1Result:
  int awaitResult1 = awaiter1.GetResult();
  awaiter1 = new TaskAwaiter<int>();
  value1 = awaitResult1;

  awaiter2 = task2.GetAwaiter();
  if (awaiter2.IsCompleted)
  {
      goto Label_GetAwaiter2Result;
  }
  state = 2;
  doFinallyBodies = false;
  awaiter2.OnCompleted(moveNextDelegate);
  return;
Label_Awaiter2Continuation:
  state = 0;
Label_GetAwaiter2Result:
  int awaitResult2 = awaiter2.GetResult();
  awaiter2 = new TaskAwaiter<int>();
  value2 = awaitResult2;

  awaiter3 = task3.GetAwaiter();
  if (awaiter3.IsCompleted)
  {
      goto Label_GetAwaiter3Result;
  }
  state = 3;
  doFinallyBodies = false;
  awaiter3.OnCompleted(moveNextDelegate);
  return;
Label_Awaiter3Continuation:
  state = 0;
Label_GetAwaiter3Result:
  int awaitResult3 = awaiter3.GetResult();
  awaiter3 = new TaskAwaiter<int>();
  value3 = awaitResult3;

  result = value1 + value2 + value3;

Like last time, I’ll just go through the interesting points this raises, rather than examining it line by line.

Switch instead of if

In all our previous async methods, we’ve only had three possible states: -1 (finished), 0 (normal), 1 (return from continuation). The generated code always looked something like this to start with:

if (state != 1) 

    if (state == -1) 
    { 
        return
    } 
    // Execute code before the first await, loop or try
}

The comment is somewhat brief here, but the basic idea is that all the code which will only ever execute the first time (with no continuations) can go here. If there’s a loop that contains an await, then a continuation would have to jump back into that loop, so that code couldn’t be contained within this initial block. (A loop which didn’t have any awaits in could though.)

Anyway, this time we don’t have an "if" statement like that – we have a switch. It’s the same idea, but we could be in any of three different states when a continuation is called, depending on which await expression we’re at. The switch statement efficiently branches to the right place for a continuation, and executes the initial code otherwise. The "branch" for state 1 is just to exit the switch statement and continue from there.

It’s possible that the generated code actually has more levels of indirection than it needs; I don’t know about the details of what’s allowed within an IL switch, but it seems odd to effectively have an "On X goto Y" where Y immediately performs a "goto Z". If the switch statement could branch immediately to the right label, we’d end up with IL which probably wouldn’t have a hope of being decompiled to C#, but which might be slightly more efficient. It’s quite likely that the JIT can sort all of that out, of course.

I tend to actually think about all of this as if the code that’s really in the "default" case for the switch statement appeared after the switch, and the default case just contained a goto statement to jump to it. The effect would be exactly the same, of course – but it means I have a mental model of the method consisting of a "jump to the right place" phase before an "execute the code" phase. Just because I think of it that way doesn’t mean you have to, of course :)

Multiple awaiters and await results

There’s room for a bit more optimization in this specific case. We have three awaiters, but they’re all of the same type (TaskAwaiter<int>). Likewise, we have three await results, but they’re all int. (It would be possible to have different awaiter types with the same result type, of course.)

In the CTP (at least without optimization enabled) we end up with an awaiter / awaitResult pair of variables for each await expression. There’s never more than one await "active" at any one time, so the C# compiler could generate one awaiter variable per awaiter type, and one result variable per result type. In the common situation where the result is being directly assigned to a "local" variable of the same type within the method, we don’t really need the result variable at all. On the other hand, it’s only a local variable (unlike the awaiter) and it’s quite possible that the JIT can optimize this instead.

Ultimately it’s entirely reasonable for the C# compiler to be generating suboptimal code at this point in the development cycle. After all, it could be quite easy to introduce bugs due to inappropriate code generation… as we’ll see next time.

Conclusion

Other than the different way of getting to the right place on entry (using a switch instead of an if statement), async methods with multiple await expressions aren’t that hard to follow. Of course when you combine multiple awaits with loops, try/catch, try/finally blocks and any number of other things you might use to complicate the async method, things become tricky – but with the fundamentals covered in this blog series, hopefully you’d be able to cope with the generated code in any reasonable situation. Of course, it’s rare that you’ll need (or want) to look at the generated code in anything like the detail we have here – but now we’ve looked at it, you don’t need to wonder where the magic happens.

The next post will be the last one involving the decompiled code, at least for the moment. I’d like to demonstrate a bug in the CTP – mostly to show you how a small change to the async method can trigger the wrong results. I’m absolutely positive it will be fixed before release – probably for the next CTP or beta – but I think it’s interesting to see the sort of situation which can cause problems.

After that, we’re going to look at exception handling, before we move into a few odd way of using async – in particular, implementing coroutines and "COMEFROM".

Eduasync part 8: generated code from a complex async method

Last time we had a really simple async method, and looked at the generated code. Even that took a little while… but in this post I’ll demonstrate some of the intricacies that get involved when the async method is more complex. In particular, this time we have:

  • A parameter to the async method
  • A "using" statement, so we’ll need a finally block
  • Two loops
  • The possibility of catching an exception generated from await and retrying

I’ve not made it as complicated as I could have done, mind you – we’ve still only got one await expression. Here’s the async method we’ll be investigating:

private static async Task<int> WriteValuesAsyncWithAssistance(int loopCount)
{
    using (TextWriter writer = File.CreateText("output.txt"))
    {
        int sum = 0;
        for (int i = 0; i < loopCount; i++)
        {
            Task<int> valueFetcher = Task.Factory.StartNew(() => 1);

            for (int j = 0; j < 3; j++)
            {
                try
                {
                    Console.WriteLine("Awaiting…");
                    int value = await valueFetcher;
                    writer.WriteLine("Got value {0}", value);
                    sum += value;
                    break; // Break out of the inner for loop
                }
                catch (Exception)
                {
                    Console.WriteLine("Oops… retrying");
                }
            }
        }
        return sum;
    }
}

Okay, so it’s already reasonably complicated even as a normal method. But it gets nastier when we look at the code generated for it. In particular, this time the decompiled code actually isn’t quite valid C#. I’ll get to that in a minute. I’ve split the rest of this post into several smallish chunks to try to help keep several concerns separated. They’re not particularly pleasantly balanced in terms of size, segue etc… but hopefully they’ll make things simpler.

The decompiled code

I’m going to ignore the standard skeleton – basically everything you see here is within a try/catch block so that we can call SetException on the builder if necessary, and if we reach the end of the method normally, we’ll call SetResult on the builder using the "result" variable. If you want to see the complete method, it’s in the source repository. Here’s the cut down version:

bool doFinallyBodies = true;
int tmpState = state;

// Effectively a three way switch: 1, -1, 0
if (tmpState != 1)
{
    if (state == -1)
    {
        return;
    }
    this.writer = File.CreateText("output.txt");
}
try
{
    tmpState = state;
    if (tmpState == 1)
    {
        goto Label_ResumePoint;
    }
    sum = 0;
    i = 0;
  Label_ResumePoint: // This shouldn’t quite be here… see below
    while (i < loopCount)
    {
        // Not in generated code:
        if (state == 1)
        {
            goto Label_ResumePoint2;
        }
        // Back to generated code

        valueFetcher = Task.Factory.StartNew(() => 1);
        j = 0;

        // Still not in the generated code, and still not quite right… we don’t want the j test here
      Label_ResumePoint2:
        // Back to generated code again…
        while (j < 3)
        {
            try
            {
                // We want Label_ResumePoint to be here really
                tmpState = state;
                if (tmpState != 1)
                {
                    Console.WriteLine("Awaiting…");
                    awaiter = valueFetcher.GetAwaiter();
                    if (!awaiter.IsCompleted)
                    {
                        state = 1;
                        doFinallyBodies = false;
                        awaiter.OnCompleted(moveNextDelegate);
                        return;
                    }
                }
                else
                {
                    state = 0;
                }
                int awaitResult = awaiter.GetResult();
                awaiter = new TaskAwaiter<int>();
                value = awaitResult;
                writer.WriteLine("Got value {0}", value);
                sum += value;
                break;
            }
            catch (Exception)
            {
                Console.WriteLine("Oops… retrying");
            }
            j++;
        }
        i++;
    }
    result = sum;
}
finally
{
    if (doFinallyBodies && writer != null)
    {
        writer.Dispose();
    }
}

Now that’s a fair bit of code… but if you were comfortable with the last post, there shouldn’t be much which is surprising. I’m not going to go over every aspect of it – just the surprising twists introduced by the extra complexities of the async method.

State and tmpState

Okay, let’s start from a point of ignorance: I don’t know why a local variable (tmpState) is introduced to take a copy of the state. My guess is that in some really complex scenarios it either becomes relevant when we want to change the state but also know what the state was, or if we’re testing the state several times without changing it, it may be quicker to do so having copied it to a local variable. I think we can pretty much ignore it – I’ve only included it here for completeness.

To do finally bodies or not to do finally bodies, that is the question

Last time the doFinallyBodies variable was pointless because we didn’t have any finally blocks – this time we do (albeit just the one). It’s implicit due to the "using" statement in the original code, but that’s really just a try/finally. The CreateText call occurs before we really enter into the using statement, which is why it’s before the try block in the decompiled code.

Broadly speaking, a try/finally block in an async method corresponds to the same try/finally block in the generated code, with one big difference: if we’re leaving the method because we’re just returning having attached a continuation to an awaiter, we don’t want to execute the finally body. After all, the method is effectively "paused" in the block rather than really exiting the block. So, any time you see an OnCompleted call, you’ll see a "doFinallyBodies = false;" statement… and that flag will be set to true at the start of the method, so that at any other time, exiting the try block will cause the finally body to be executed. We still want the body to execute if there’s an exception, for example. This is the same pattern used by the generated code for iterator blocks. (That’s extremely unsurprising at the moment, as the code generators share a lot of the code. It’s one bit which will have to be preserved if the two generators are split though.)

Go to where exactly?

As I mentioned earlier, we can’t write C# code which exactly represented the generated IL. That’s because when we enter the method in state 1 (i.e. for a continuation) the actual generated IL performs a goto straight into the start of the try block. You can’t do that in straight C#, which is why I’ve got an ugly double-goto in my representation. It doesn’t make it a 100% accurate representation of the IL, but it’s close enough for our purposes. If you build the async method and look at the generated code in Reflector, you’ll see that it basically shows a jump to the final comment above, just inside the try block. (Reflector actually shows the label as occurring before the try block, but the branch instruction in the IL really does refer to the first instruction in the try block.)

Now, you may be wondering why the generated code only jumps to the start of the try block. Why doesn’t it just jump straight to the "await" part? Last time it jumped straight to the "state = 0;" statement, after all… so we might expect it to do the same thing here.

It turns out that although IL has somewhat more freedom than C# in terms of goto (branch) statements, it’s not entirely free of restrictions. In this case, I believe it’s running up against section 12.4.2.8.2.7 of ECMA-335 (the CLI specification) which states:

If any target of the branch is within a protected block, except the first instruction of that protected block, the source shall be within the same protected block.

[…]

[Note: Code can branch to the first instruction of a protected block, but not into the middle of one. end note]

For "protected block" we can read "try block" (at least in this case).

I really shouldn’t enjoy the fact that even the C# compiler can’t do exactly what it would like to here, but somehow it makes me feel better about the way that the C# language restricts us. It’s possible that anthropomorphizing the C# compiler at all is a bad sign for my mental state, but never mind. It gets even worse if an await expression occurs in a nested try block – another "if" is required for each level involved.

The point is that by hook or by crook, when a continuation is invoked, we need to reach the assignment back to state 0, followed by the call to awaiter.GetResult().

Why do we go back to state 0?

You may have noticed that when we return from a continuation, we set the state to 0 before we call GetResult(). We only ever set to the state to a non-zero value just adding a continuation, or when we’ve finished (at which point we set it to -1). Therefore it will actually be 0 whenever we call GetResult() on an awaiter. But why? Shouldn’t state 0 mean "we’re just starting the method"? We know that state -1 means "the method has finished" and a positive state is used to indicate where we need to come back to when the continuation is called… so what sense does it make to reuse state 0?

This bit took me a while to work out. In fact, it had me so stumped that I asked a question on Stack Overflow about it. Various theories were presented, but none was entirely satisfactory. At the same time, I mailed Mads Torgersen and Lucian Wischik about it. After a while, Lucian got back to me and assured me that this wasn’t a bug – it was very deliberate, and absolutely required. He didn’t say why though – he challenged me to work it out, just in the knowledge that it was required. The example I eventually came up with was a slightly simpler version of the async method in this post.

The key is to think of states slightly differently:

  • -1: finished
  • 0: running normally; no special action required
  • Anything else: Waiting to get back to a continuation point (or in the process of getting there)

So why must we go back to the "running normally" state as soon as we’ve reached the right point when running a continuation? Well, let’s imagine the flow in a situation like the async method shown, but where instead of just creating a trivial task, we’d started one which could fail, and does… but hasn’t finished before we first await it. Ignoring the outer loop and the using statement, we have something like this:

  1. The task is created
  2. We enter the inner loop
  3. We write out "Awaiting…" on the console
  4. We get an awaiter for the task, and check whether it’s completed: it hasn’t, so we add a continuation and return
  5. The task completes, so we call GetResult() on the awaiter
  6. GetResult() throws an exception, so we log it in the catch block
  7. We go back round the inner loop, and want to re-enter step 3

Now look at the flow control within the decompiled method again, and imagine if we hadn’t set the state to 0. We’d still be in state 1 when we reached the catch block, so when we got back to the top of the loop, we’d ignore the Console.WriteLine call and not even get a fresh awaiter – we’d end up reusing the same awaiter again. In other words, we’d try to get back to the continuation point.

To think of it another way: by the time we get back to the call to awaiter.GetResult(), the two possible paths (executing immediately vs adding a continuation, based on the value of awaiter.IsCompleted) have merged and we want to behave exactly the same way from that point onwards. So naturally we’d want to be in the same state… which is 0 for the synchronous path, as we haven’t had any reason to change state.

Conclusion

All this complexity, and just with one await expression! As is so often true in computing, it all seems a bit simpler when you can look past it being a wall of code, and break down each lump separately. Hopefully I’ve taken some of the surprises out of the generated code here, although please do leave a comment if anything is still unclear – I’ll try to update the post if so.

Next time we’ll look at an async method which is generally simpler, but which has three await expressions.

Eduasync part 7: generated code from a simple async method

In part 6 we tried to come up with a "manual" translation of a very simple async method. This time we’ll see what the compiler really generates.

As a reminder, here’s the async method in question:

private static async Task<int> ReturnValueAsyncWithAssistance() 

    Task<int> task = Task<int>.Factory.StartNew(() => 5); 

    return await task; 
}

Whereas our manual code still ended up with a single method (admittedly involving a lambda expression), the generated code is split into the method itself (which ends up being quite small), and a whole extra class representing a state machine.

The "stub" method

We looked at a similar translation before, but didn’t really explain it. This time let’s take a closer look:

private static Task<int> ReturnValueAsyncWithStateMachine()
{
    StateMachine stateMachine = new StateMachine(0);
    stateMachine.moveNextDelegate = stateMachine.MoveNext;
    stateMachine.builder = AsyncTaskMethodBuilder<int>.Create();
    stateMachine.MoveNext();
    return stateMachine.builder.Task;
}

Note that I’ve renamed all the variables and the generated type itself to be valid C# identifiers with useful names.

This is pretty much a minimal stub from an async method. If the async method took parameters, each parameter value would end up being copied into a field in the state machine – but other than that, this is what all async methods end up looking like. (Void async methods just won’t return the builder’s task at the end.)

The method doesn’t have much work to do:

  • It creates a new instance of the state machine, passing in an initial state of 0. (I’d expect this constructor parameter to be removed by release – it’ll always be 0, after all.)
  • It creates a new Action delegate instance corresponding to the MoveNext method in the state machine. This is always passed to awaiters as the continuation – by using a field in the state machine, we can avoid creating a new Action instance each time we call OnCompleted.
  • It creates a new AsyncTaskMethodBuilder which the state machine uses to set the result and/or exception, and which exposes a task we can return to the caller.
  • It calls MoveNext() to synchronously: this will execute at least as far as the first "await" before returning, and may get further than that if the awaited tasks have completed before the await expression.
  • It returns the Task<int> from the builder. By this point there may already be a result, if all the await expressions had already completed. In real async methods, most of the time the returned Task or Task<T> will represent an ongoing task which the state machine will keep track of.

Obviously, all the smarts are in the state machine…

The state machine (completed)

Here’s the complete code of the generated state machine, as of the second CTP of the async feature.

[CompilerGenerated]
private sealed class StateMachine
{
    // Fields representing local variables
    public Task<int> task;

    // Fields representing awaiters
    private TaskAwaiter<int> awaiter;

    // Fields common to all async state machines
    public AsyncTaskMethodBuilder<int> builder;
    private int state;
    public Action moveNextDelegate;

    public StateMachine(int state)
    {
        // Pointless: will always be 0. Expect this to be removed from later builds.
        this.state = state;
    }

    public void MoveNext()
    {
        int result;
        try
        {
#pragma warning disable 0219 // doFinallyBodies is never used
            bool doFinallyBodies = true;
#pragma warning restore
            if (state != 1)
            {
                if (state != -1)
                {
                    task = Task<int>.Factory.StartNew(() => 5);
                    awaiter = task.GetAwaiter();
                    if (awaiter.IsCompleted)
                    {
                        goto Label_GetResult;
                    }
                    state = 1;
                    doFinallyBodies = false;
                    awaiter.OnCompleted(moveNextDelegate);
                }
                return;
            }
            state = 0;
          Label_GetResult:
            int awaitResult = awaiter.GetResult();
            awaiter = new TaskAwaiter<int>();
            result = awaitResult;
        }
        catch (Exception e)
        {
            state = -1;
            builder.SetException(e);
            return;
        }
        state = -1;
        builder.SetResult(result);
    }

    // Obsolete: will be removed from later builds.
#pragma warning disable 0414
    private bool disposing;
#pragma warning restore

    [DebuggerHidden]
    public void Dispose()
    {
        disposing = true;
        MoveNext();
        state = -1;
    }
}

Yes, it’s pretty complicated. Let’s break it down a bit…

Unused code

Some of the code here is pointless. It’s a side-effect of the fact that for the CTP, which uses the same code for iterator blocks and async methods – that’s why the primary method within the state machine is called MoveNext. So everything below the comment line starting "Obsolete" can be ignored.

Additionally, in this case we don’t have any finally blocks – so the doFinallyBodies local variable is unused too. Later on we’ll see an example which does use it, so don’t worry about it for now.

Fields and initialization

I’ve explained all the fields in the comments, to some extent. Broadly speaking, there are three types of field in async method translations:

  • Variables for the infrastructure:
    • The state number, used to work out where to jump back into the method when the continuation is called (or on our first call from the stub method)
    • The builder, used to communicate with the task returned to the caller, via SetResult and SetException
    • The moveNextDelegate, always passed to the OnCompleted method when an awaiter indicates an incomplete task
  • Awaiter variables: each await expression (currently) has its own awaiter variable. (This may be optimized in a later build.) Each one is set back to the type’s default value (e.g. null for a reference type) after use, but we need to maintain this as a field so we can get the result of an awaiter when we return from a continuation.
  • "User" variables representing the local variables of the async method. If the method uses language features such as collection initializers, there may be more of these than expected, but basically they’re whatever you’d see as local variables in a normal method. This also includes parameters for the async method, which are copied into the state machine by the stub method.

Standard skeleton

All async methods (well, those returning results) have a standard skeleton for the MoveNext() method. Void async methods differ in the obvious way. You may recognise this as being extremely similar to part of our manually-written method, too:

public void MoveNext()
{
    int result; // Type varies based on result type
    try
    {
        // Method-specific code
    }
    catch (Exception e)
    {
        state = -1;
        builder.SetException(e);
        return;
    }
    state = -1;
    builder.SetResult(result);
}

Hopefully this doesn’t need much explanation: while catching Exception is normally a bad idea, it’s appropriate here, so that it can be rethrown in the caller’s context (or detected without being rethrown). Obviously the method-specific code in the middle needs to make sure that we only reach the bottom of the method in the case where we’re really done – if it ever calls OnCompleted, it needs to return directly rather than falling through to this code.

Actual method-specific code…

Phew, we’ve finally got to the bit which varies depending on the async method we’re dealing with. Fortunately now that we’ve removed all the extraneous code, it’s easier to see what’s going on. I’ve added some comments for clarity:

  if (state != 1)
  {
      if (state != -1)
      {
          // Code before the first await expression
          task = Task<int>.Factory.StartNew(() => 5);

          // Boiler-plate for the first part of an await
          awaiter = task.GetAwaiter();
          if (awaiter.IsCompleted)
          {
              goto Label_GetResult;
          }
          state = 1;
          doFinallyBodies = false;
          awaiter.OnCompleted(moveNextDelegate);
      }
      return;
  }
  // Boiler-plate for the second part of an await
  state = 0;
Label_GetResult:
  int awaitResult = awaiter.GetResult();
  awaiter = new TaskAwaiter<int>();
                    
  // Code after the await (return, basically)
  result = awaitResult;

Now the state machine for our async method can be in any of three states at the start/end of MoveNext():

  • -1: the method has already completed (possibly with an exception). (It’s unclear how we’d end up getting back into the state machine at this point, but the generated code makes sure it just returns immediately.)
  • 0: The initial state this is also used just before calling GetResult() for situations where we’ve not needed to use a continuation. I’ll go into this slight oddity in another post :)
  • 1: We’re waiting to be called back by the awaiter. When the continuation is executed, we want to jump straight to "Label_GetResult" so we can fetch the result and continue with the rest of the method.

I suggest you look at the code path taken by the above code if you start in each of those states – convince yourself that when we exit the method, we’ll be in an appropriate state, whether it’s normally or via a continuation.

Even within this code, we can see boilerplate material: any time you reach an await expression in an async method, the compiler will generate code like the middle section, to get the awaiter, then either skip to the "get result" part or add a continuation. The fiddly bit is really getting your head round how the code flow works, but hopefully as there are relatively few states in play here, it’s not too bad.

Conclusion

This was a very long post for such a short async method. However, now that we’ve sorted out how it all hangs together, we should be able to look at more complicated async methods without looking at things like the obsolete code and the stub method. Next time we’ll see what happens when we introduce loops, catch blocks and finally blocks.