Tuesday 14 December 2010

C# 5, Async Exceptions Mysteries

 

Studying the CTP of C# 5 I discovered that an asynchronous method will throw away anything other than the first exception in an AggregateException thrown by one of the tasks it's waiting for. Reading the TAP documentation, it seems this is partlyexpected behaviour and partly not. TAP claims (in a section about how "await" is achieved by the compiler):

It is possible for a Task to fault due to multiple exceptions, in which case only one of these exceptions will be propagated; however, the Task’s Exception property will return an AggregateException containing all of the errors.
Unfortunately, that appears not to be the case. Here's a test program demonstrating the difference between an async method and a somewhat-similar manually written method.

static async Task ThrowMultipleAsync()
{
   Task t1 = DelayedException(500);
   Task t2 = DelayedException(1000);
   await TaskEx.WhenAll(t1,t2);
}

static Task ThrowMultipleManually()
{
   Task t1 = DelayedException(500);
   Task t2 = DelayedException(1000);
   return TaskEx.WhenAll(t1, t2);
}

static Task DelayedException(Int32 millisecs)
{
   return TaskEx.Run(delegate
   {
      Tread.Sleep(millisecs);
      throw new Exception("Exception thrown after" + millisecs);
   });
}
The difference is that the async method is generating an extra task, instead of returning the task from TaskEx.WhenAll. It's waiting for the result of WhenAll itself (via EndAwait). The results show one exception being swallowed:
Waiting for From async method
Thrown exception: 1 error(s):
Exception thrown after 500

Task exception: 1 error(s):
Exception thrown after 500

Waiting for From manual method
Thrown exception: 2 error(s):
Exception thrown after 500
Exception thrown after 1000

Task exception: 2 error(s):
Exception thrown after 500
Exception thrown after 1000
The fact that the "manual" method still shows two exceptions means we can't blame WhenAll - it must be something to do with the async code. Given the description in the TAP documentation, I'd expect (although not desire) the thrown exception to just be a single exception, but the returned task's exception should have both in there. That's clearly not the case at the moment.
Anyone any ideas? If not then I am going back to my dungeon...

No comments:

Post a Comment