mark
mark

Reputation: 62764

How to let exception propagate up the stack from async methods? (cannot use async or await keywords)

Please, observe this simple code:

   try
   {
     var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
     t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion).Wait();
   }
   catch (Exception exc)
   {
     Debug.WriteLine(exc);
   }

I assumed that if t has an exception associated with it, then this exception will be rethrown as is by Wait(). However, the presence of the success only continuation seems to change this behavior. What is thrown instead is a "A task was canceled" exception.

Indeed, chaining a TaskContinuationOptions.NotOnRanToCompletion completion handler just before Wait() reveals that the task passed to it is not faulted, but cancelled:

t.ContinueWith(_ => { }, TaskContinuationOptions.OnlyOnRanToCompletion)
 .ContinueWith(t2 => Debug.Assert(t2.IsCanceled), TaskContinuationOptions.NotOnRanToCompletion)
 .Wait();

This is all a bit strange. It means, I cannot just chain my happy path completion handlers letting any exceptions just propagate to the ultimate rendezvous with the waiting thread.

What am I missing here?

NOTE

I am limited to .NET 4.0, so no await and async keywords.

Upvotes: 1

Views: 362

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156524

What am I missing here?

You are missing .NET 4.5. Tasks are still useful without the await and async keywords, but if you want the "happy path" behavior you're talking about, you'll need to upgrade (see update below).

Because tasks are more complicated than standard run-through code, and because they can be joined together in various ways, you'll need to ask for the exception directly from the Task, rather than the exception thrown by the call to .Wait().

var t = Task.Factory.StartNew<bool>(() => { throw new Exception("aaa"); });
   try
   {
     t.ContinueWith(_ => {}, TaskContinuationOptions.OnlyOnRanToCompletion)
        .Wait();
   }
   catch (AggregateException exc)
   {
     Debug.WriteLine(exc.InnerExceptions[0]);// "A task was canceled"
     Debug.WriteLine(t.Exception.InnerExceptions[0]);// "aaa"
   }

Update: If you are using Visual Studio 2012, it appears that you can use the async and await keywords without upgrading to 4.5. Thanks to @zespri for pointing this out.

Update 2: If you want to catch and log the appropriate exception at the top level, just make a habit of wrapping your .Wait() methods in try/catch blocks, wrap the given exception.

try
{
  t.Wait();
}
catch (AggregateException exc)
{
  throw new Exception("Task Foo failed to complete", t.Exception);
}

Upvotes: 2

Related Questions