Category Archives: C# 5

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.

Eduasync part 6: using the infrastructure manually

Now that we’ve got the infrastructure for both returning a task from an async method, and awaiting a Task<T>, we’re going to look at what the compiler does for us. I always find that the best way of appreciating a feature like this is to consider what we’d have to do without it. Later on we’re going to see what the C# compiler does in terms of decompiling the actual generated code, but first we’ll look at what we might do to achieve the same end result, using the same infrastructure.

Of course, if we were doing all of this manually we might not choose to use the same infrastructure to start with, but I’m not sure that it would end up being much cleaner anyway.

A simple async method

First, let’s look at the C# 5 async method we want to emulate. It’s pretty simple:

private static async Task<int> ReturnValueAsyncWithAssistance()
{
    Task<int> task = Task<int>.Factory.StartNew(() => 5);

    return await task;
}

Now obviously we could just return the task here to start with – the async method isn’t providing any real benefit. But the simplicity is helpful for showing an equivalent. You can always imagine that there’s more going on between awaiting the result of the task and returning our actual result.

What do we need to do?

Before I include the actual code, let’s consider what we know about the async method execution model:

  • We’ll create an AsyncTaskMethodBuilder to allow us to return a Task<int>.
  • The method will start synchronously, running all the code before the first (and in this case only) await expression in the normal way.
  • The awaitable expression will be evaluated, and GetAwaiter() called to get a suitable awaiter value.
  • We’ll ask the awaiter whether the awaitable has already completed, using the IsCompleted property
    • If it’s already completed, we get the result by calling GetResult(). We can set the result on the builder, and return the builder’s task.
    • Otherwise, create a continuation delegate representing "the rest of the method". We then return the builder’s task.
      • The continuation has to fetch the result by calling GetResult() on the same awaiter, set the result on the builder, and return. (It’s not returning a Task<T> – the continuation is just an Action.)
  • If an exception occurs at any point, whether in the original synchronous call or the continuation, we need to set that exception in the builder, and return (either returning the builder’s task or nothing, depending on the context).

Even describing it makes it sound significantly worse than the async method. Let’s take a look.

Bring on the code!

Here’s the manual method in all its glory:

private static Task<int> ReturnValueAsyncWithoutAssistance()
{
    AsyncTaskMethodBuilder<int> builder = AsyncTaskMethodBuilder<int>.Create();

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

        TaskAwaiter<int> awaiter = task.GetAwaiter();
        if (!awaiter.IsCompleted)
        {
            // Result wasn’t available. Add a continuation, and return the builder.
            awaiter.OnCompleted(() =>
            {
                try
                {
                    builder.SetResult(awaiter.GetResult());
                }
                catch (Exception e)
                {
                    builder.SetException(e);
                }
            });
            return builder.Task;
        }

        // Result was already available: proceed synchronously
        builder.SetResult(awaiter.GetResult());
    }
    catch (Exception e)
    {
        builder.SetException(e);
    }
    return builder.Task;
}

Ugly, isn’t it? Even so, it’s actually somewhere simpler than the real autogenerated code, which we’ll see in the next part. Hopefully if you read the code and comments bearing in mind the bullet points above, it should be reasonably easy to see how it works. However:

  • Currently we’re building a continuation delegate specific to this particular await. Imagine if we had several await expressions. Each would need "the rest of the method" specified as its own delegate, and we’d end up building multiple delegate instances over time.
  • It would be a nightmare to handle loops in this way.
  • We’ve got repeated code – not a lot in this case, but anything we had between the "await" and the "return" would need to be in both flows.
  • You don’t really want to be writing this code by hand anyway, if you can help it.

Having said all of that, it works. Just don’t expect me to write any more complicated async methods this way :)

Conclusion

The steps given here are still going to be valid with the compiler-generated code, but they’ll take a slightly different form. Next time we’ll decompile the generated code for the exact same async method, before we move on to look at more complicated cases.

Eduasync part 5: making Task awaitable

In part 3 we looked at what the C# 5 compiler required for you to "await" something. The sample used a class which actually had an instance method called GetAwaiter, but I mentioned that it could also be an extension method.

In this post, we’ll use that ability to make Task<T> awaitable – at which point we have everything we need to actually see some asynchronous behaviour. Just like the last part, the code here is pretty plain – but by the end of the post we’ll have a full demonstration of asynchrony. I should make it clear that this isn’t absolutely everything we’ll want, but it’s a good start.

TaskAwaiter<T>

As it happens, I’m using the same type name as the async CTP does for "something which can await a task" – TaskAwaiter. It’s in a different namespace though, and we could rename it with no ill-effects (unlike AsyncTaskMethodBuilder, for example). Indeed, in an old version of similar code I called this type Garçon – so GetAwaiter would return a waiter, so to speak. (Yes, you can use non-ASCII characters in identifiers in C#. I wouldn’t really advise it though.)

All the task awaiter needs is a reference to the task that it’s awaiting – it doesn’t need to know anything about the async method which is waiting for it, for example. Task<T> provides everything we need: a property to find out whether it’s completed, another to fetch the result, and the ContinueWith method to specify a continuation. The last part is particularly important – without that, we’d really have a hard time implementing this efficiently.

The extension method on Task<T> is trivial:

public static class TaskExtensions
{
    public static TaskAwaiter<T> GetAwaiter<T>(this Task<T> task)
    {
        return new TaskAwaiter<T>(task);
    }
}

The TaskAwaiter<T> type itself is slightly less so, but only slightly:

public struct TaskAwaiter<T>
{
    private readonly Task<T> task;

    internal TaskAwaiter(Task<T> task)
    {
        this.task = task;
    }

    public bool IsCompleted { get { return task.IsCompleted; } }

    public void OnCompleted(Action action)
    {
        SynchronizationContext context = SynchronizationContext.Current;
        TaskScheduler scheduler = context == null ? TaskScheduler.Current
            : TaskScheduler.FromCurrentSynchronizationContext();
        task.ContinueWith(ignored => action(), scheduler);
    }

    public T GetResult()
    {
        return task.Result;
    }
}

IsCompleted is obviously trivial – Task<T> provides us exactly what we need to know. It’s just worth noting that IsCompleted will return true if the task is cancelled, faulted or completed normally – it’s not the same as checking for success. However, it represents exactly what we want to know here.

OnCompleted has two very small aspects of interest:

  • ContinueWith takes an Action<Task<T>> or an Action<Task>, not just an Action. That means we have to create a new delegate to wrap the original continuation. I can’t currently think of any way round this with the current specification, but it’s slightly annoying. If the compiler could work with an OnCompleted(Action<object>) method then we could pass that into Task<T>.ContinueWith due to contravariance of Action<T>. The compiler could generate an appropriate MoveNext(object) method which just called MoveNext() and stash an Action<object> field instead of an Action field… and do so only if the async method actually required it. I’ll email the team with this as a suggestion – they’ve made other changes with performance in mind, so this is a possibility. Other alternatives:
    • In .NET 5, Task<T> could have ContinueWith overloads accepting Action as a continuation. That would be simpler from the language perspective, but the overload list would become pretty huge.
    • I would expect Task<T> to have a "real" GetAwaiter method in .NET 5 rather than the extension method; it could quite easily just return "this", possibly with some explicitly implemented IAwaiter<T> interface to avoid polluting the normal API. That could then handle the situation more natively.
  • We’re using the current synchronization context if there is one to schedule the new task. This is the bit that lets continuations keep going on the UI thread for WPF and WinForms apps. If there isn’t a synchronization context, we just use the current scheduler. For months this was incorrect in Eduasync; I was using TaskScheduler.Current in all cases. It’s a subtle difference which has a huge effect on correctness; apologies for the previous inaccuracy. Even the current code is a lot cruder than it could be, but it should be better than it was…

GetResult looks and is utterly trivial – it works fine for success cases, but it doesn’t do what we really want if the task has been faulted or cancelled. We’ll improve it in a later part.

Let’s see it in action!

Between this and the AsyncTaskMethodBuilder we wrote last time, we’re ready to see an end-to-end asynchronous method demo. Here’s the full code – it’s not as trivial as it might be, as I’ve included some diagnostics so we can see what’s going on:

internal class Program
{
    private static readonly DateTimeOffset StartTime = DateTimeOffset.UtcNow;

    private static void Main(string[] args)
    {
        Log("In Main, before SumAsync call");
        Task<int> task = SumAsync();
        Log("In Main, after SumAsync returned");

        int result = task.Result;
        Log("Final result: " + result);
    }

    private static async Task<int> SumAsync()
    {
        Task<int> task1 = Task.Factory.StartNew(() => { Thread.Sleep(500); return 10; });
        Task<int> task2 = Task.Factory.StartNew(() => { Thread.Sleep(750); return 5; });

        Log("In SumAsync, before awaits");
           
        int value1 = await task1;
        int value2 = await task2;

        Log("In SumAsync, after awaits");

        return value1 + value2;
    }

    private static void Log(string text)
    {
        Console.WriteLine("Thread={0}. Time={1}ms. Message={2}",
                          Thread.CurrentThread.ManagedThreadId,
                          (long)(DateTimeOffset.UtcNow – StartTime).TotalMilliseconds,
                          text);
    }
}

And here’s the result of one run:

Thread=1. Time=12ms. Message=In Main, before SumAsync call
Thread=1. Time=51ms. Message=In SumAsync, before awaits
Thread=1. Time=55ms. Message=In Main, after SumAsync returned
Thread=4. Time=802ms. Message=In SumAsync, after awaits
Thread=1. Time=802ms. Message=Final result: 15

So what’s going on?

  • We initially log before we even start the async method. We can see that the thread running Main has ID 1.
  • Within SumAsync, we start two tasks using Task.Factory.StartNew. Each task just has to sleep for a bit, then return a value. Everything’s hard-coded.
  • We log before we await anything: this occurs still on thread 1, because async methods run synchronously at least as far as the first await.
  • We hit the first await, and because the first task hasn’t completed yet, we register a continuation on it, and immediately return to Main.
  • We log that we’re in Main, still in thread 1.
  • When the first await completes, a thread from the thread pool will execute the continuation. (This may well be the thread which executed the first task; I don’t know the behaviour of the task scheduler used in console apps off the top of my head.) This will then hit the second await, which also won’t have finished – so the first continuation completes, having registered a second continuation, this time on the second task. If we changed the Sleep calls within the tasks, we could observe this second await actually not needing to wait for anything.
  • When the second continuation fires, we log that fact. Two things to notice:
    • It’s almost exactly 750ms after the earlier log messages. That proves that the two tasks has genuinely been executing in parallel.
    • It’s on thread 4.
  • The final log statement occurs immediately after we return from the async method – thread 1 has been blocked on the task.Result property fetch, but when the async method completes, it unblocks and shows the result.

I think you’ll agree that for the very small amount of code we’ve had to write, this is pretty nifty.

Conclusion

We’ve now implemented enough of the functionality which is usually in AsyncCtpLibrary.dll to investigate what the compiler’s really doing for us. Next time I’ll include a program showing one option for using the same types within hand-written code… and point out how nasty it is. Then for the next few parts, we’ll look at what the C# 5 compiler does when we let it loose on code like the above… and show why I didn’t just have "int value = await task1 + await task2;" in the sample program.

If you’ve skimmed through this post reasonably quickly, now would be a good time to go back and make sure you’re really comfortable with where in this sample our AsyncTaskMethodBuilder is being used, and where TaskAwaiter is being used. We’ve got Task<T> as the core type at both boundaries, but that’s slightly coincidental – the boundaries are still very different, and it’s worth making sure you understand them before you try to wrap your head round the compiler-generated code.

Eduasync part 4: Filling in AsyncTaskMethodBuilder

In part 2, I introduced AsyncVoidMethodBuilder, AsyncTaskMethodBuilder and AsyncTaskMethodBuilder<T>. I showed all the signatures, but the implementations were basically trivial and non-functional. Today, I’m going to change all of that… at least a bit.

In particular, by the end of this post we’ll be able to make the following code actually print out 10 as we’d expect:

private static void Main(string[] args)
{
    Task<int> task = Return10Async();
    Console.WriteLine(task.Result);
}

private static async Task<int> Return10Async()
{
    return 10;
}

Note that there are no await statements – remember that AsyncTaskMethodBuilder<T> is about the boundary between the caller and the async method. We won’t be actually doing anything genuinely asynchronous today – just filling in a piece of the infrastructure.

I’m not going to implement AsyncVoidMethodBuilder or AsyncTaskMethodBuilder, because once you understand how AsyncTaskMethodBuilder<T> works, the others are basically trivial to implement. (Certainly the non-generic AsyncTaskMethodBuilder is too similar to warrant going into; I may potentially revisit AsyncVoidMethodBuilder to investigate its behaviour on exceptions.)

Just as a reminder, this is what we need our AsyncTaskMethodBuilder<T> struct to look like in terms of its interface:

public struct AsyncTaskMethodBuilder<T>
{
    public static AsyncTaskMethodBuilder<T> Create();
    public void SetException(Exception e);
    public void SetResult(T result);
    public Task<T> Task { get; }
}

Frankly, this would be a bit of a pain to implement ourselves… especially if we wanted to do it in a neat way which didn’t introduce a new thread unnecessarily. Fortunately, Parallel Extensions makes it pretty easy.

TaskCompletionSource to the rescue!

The TaskCompletionSource<TResult> type in the framework makes it almost trivial to implement AsyncTaskMethodBuilder<T>. It provides us everything we need – a task, and the ability to specify how it completes, either with an exception or a value. Our initial implementation is really just going to wrap TaskCompletionSource. I suspect the real CTP does slightly more work, but you can get a remarkably long way with simple wrapping:

public struct AsyncTaskMethodBuilder<T>
{
    private readonly TaskCompletionSource<T> source;

    private AsyncTaskMethodBuilder(TaskCompletionSource<T> source)
    {
        this.source = source;
    }

    public static AsyncTaskMethodBuilder<T> Create()
    {
        return new AsyncTaskMethodBuilder<T>(new TaskCompletionSource<T>());
    }

    public void SetException(Exception e)
    {
        source.SetException(e);
    }

    public void SetResult(T result)
    {
        source.SetResult(result);
    }

    public Task<T> Task { get { return source.Task; } }
}

We’ll see how far that takes us – we may need to tweak it a bit, probably around the exception handling. For the moment though, it certainly does everything we need it to. The program runs, and prints out 10 as we’d expect.

This part of the code is now ready for asynchrony – even though nothing we’ve done so far actually creates any different threads. If we call SetResult from one thread while another thread is waiting on the task’s result, the waiting thread will be unblocked as we’d expect.

Conclusion

In some ways I hope you’re disappointed by this, in the same way that looking at the LINQ to Objects implementations can be slightly disappointing. It feels like so much is being given to us for free – the framework already had all this power, so what’s C# 5 really adding to the mix. Well, we’re nearly at the point where I can show you the details of what the compiler’s doing under the hood. That’s where the real power lies, and is why I’m so excited by the feature.

First though, we need to implement the awaiter pattern for Task<T>. That’s the task of our next post, and then we can complete the picture by decompiling the generated code for some programs using genuine asynchrony.

Eduasync part 3: the shape of the async method / awaitable boundary

Last time we looked into the boundary between the caller of an async method and the method itself.

This time I’m going to show the same sort of "skeleton API" but for awaitable types. This is at the heart of C# 5’s async feature: within an async method, you can include "await" expressions. You have to await a value – which could be the return value from a method call, or a property, or the value of a variable. The important thing is that the compile-time type of that expression has the right shape.

Definition time

Consider this code snippet from an async method (separated into two statements for clarity):

Task<string> t = new WebClient().DownloadStringTaskAsync(url);
string text = await t;

I wish to define the following terms:

  • The task of the await expression is the expression which comes after "await"; in this case it’s just "t".
  • The awaitable type is the compile-time type of the task. In this case it’s Task<string>.
  • The awaiter type is the compile-time type of the result of calling GetAwaiter() on the task.
  • The result type is the compile-time return type of the awaiter type’s GetResult method. Here it would be string, when using the CTP or any sensible implementation.

(Of these, "task" and "awaiter type" are part of the draft specification. "Awaitable type" and "result type" aren’t.)

Now, the first two are fairly clear from the code above, but the awaiter and result types aren’t. The compiler validates that calling (foo).GetAwaiter() for a task expression "foo" is valid. It doesn’t have to be an instance method – it can be an extension method, and indeed that’s how it’s implemented in the CTP. Unlike the weird things you can do with LINQ query expressions, GetAwaiter() really does have to be a method call, and the expression can’t just be a type name. Unless you’re trying to do weird stuff, this is very unlikely to be an issue for you. It’s only oddballs like me who try to push the envelope.

With the current CTP the awaitable type can’t be "dynamic", but the aim is for C# 5 to support awaiting dynamic values. I imagine this won’t affect the execution flow significantly though.

The awaitable type only has to support the GetAwaiter() method, as we’ve said. The awaiter type has to have the following accessible members (which can’t be extension methods):

  • bool IsCompleted { get; }
  • void OnCompleted(Action continuation)
  • X GetResult() where X defines the result type for the awaiter, and may be void

The overall type of the await expression is the result type – and it has to be appropriate for any surrounding context. For example, the assignment to the "text" variable will only work in our sample if the result type is "string" or a type with an implicit conversion to string.

A simple (non-functional) awaiter

The sample code for this post comes from project 5 in the source solution (SimpleInt32Awaitable); project 4 is similar but with a void result type. Neither project makes any attempt to do any real awaiting:

// Within some other class
static async void SimpleWaitAsync()
{
    SimpleInt32Awaitable awaitable = new SimpleInt32Awaitable();
    int result = await awaitable;
}
 

public struct SimpleInt32Awaitable
{
    public SimpleInt32Awaiter GetAwaiter()
    {
        return new SimpleInt32Awaiter();
    }
}

public struct SimpleInt32Awaiter
{
    public bool IsCompleted { get { return true; } }

    public void OnCompleted(Action continuation)
    {
    }

    public int GetResult()
    {
        return 5;
    }
}

Note that here both the awaitable type and the awaiter type are structs rather than classes; this is not a requirement by any means.

What does it all mean?

I don’t want to leave this post without any clue of what all of this is used for.

Fairly obviously, GetAwaiter() is called to get an awaiter (clever name, huh?) on which some members are called:

  • IsCompleted is called to determine whether the task has already completed. If it has, we can skip over the next step.
  • If the task hasn’t already completed, we want to return very soon rather than waiting – so OnCompleted is calling with a delegate representing the continuation of the method. The awaiter promises to ensure that the continuation is called when (and only when) the task has completed, possibly with an exception.
  • When we know the task has completed (either synchronously or via the continuation), GetResult is called to retrieve the result. Note that even if the method has a void return type, it still has to be called, as the "result" may be that an exception is thrown.

Conclusion

Awaiters are pretty flexible – which means they can be abused in evil, horrible ways, as we’ll see later on.

So far we’ve seen the basic shape of both boundaries, and now we can start actually implementing them with real, useful code.