Will awaiting multiple tasks observe more than the first exception? - c#

Today my colleagues and I discussed how to handle exceptions in C# 5.0 async methods correctly, and we wondered if awaiting multiple tasks at once also observes the exceptions that do not get unwrapped by the runtime.
Consider the following code snippet:
async Task ExceptionMethodAsync()
{
await Task.Yield();
throw new Exception();
}
async Task CallingMethod()
{
try
{
var a = ExceptionMethodAsync();
var b = ExceptionMethodAsync();
await Task.WhenAll(a, b);
}
catch(Exception ex)
{
// Catches the "first" exception thrown (whatever "first" means)
}
}
What happens to the second task now? Both will be in a faulted state, but is the second task's exception now observed or unobserved?

Task.WhenAll returns a task and like all tasks the Exception property holds an AggregateException that combines all exceptions.
When you await such a task only the first exception will actually be thrown.
... Whether because of child tasks that fault, or because of combinators like Task.WhenAlll, a single task may represent multiple operations, and more than one of those may fault. In such a case, and with the goal of not losing exception information (which can be important for post-mortem debugging), we want to be able to represent multiple exceptions, and thus for the wrapper type we chose AggregateException.
... Given that, and again having the choice of always throwing the first or always throwing an aggregate, for “await” we opt to always throw the first
from Task Exception Handling in .NET 4.5
It's up to you to choose if you want to handle just the first using await task; (true in most cases) or handle all using task.Exception (as in my example below), but in both cases a and b would not raise an UnobservedTaskException.
var task = Task.WhenAll(a, b);
try
{
await task;
}
catch
{
Trace.WriteLine(string.Join(", ", task.Exception.Flatten().InnerExceptions.Select(e => e.Message)));
}

Related

Is there any other way to set Task.Status to Cancelled

Ok, so I understand how to do Task cancellations using CancellationTokenSource. it appears to me that the Task type "kind of" handles this exception automatically - it sets the Task's Status to Cancelled.
Now you still actually have to handle the OperationCancelledException. Otherwise the exception bubbles up to Application.UnhandledException. The Task itself kind of recognizes it and does some handling internally, but you still need to wrap the calling code in a try block to avoid the unhandled exception. Sometimes, this seems like unnecessary code. If the user presses cancel, then cancel the Task (obviously the task itself needs to handle it too). I don't feel like there needs to be any other code requirement. Simply check the Status property for the completion status of the task.
Is there any specific reason for this from a language design point of view? Is there any other way to set the Status property to cancelled?
You can set a Task's status to cancelled without a CancellationToken if you create it using TaskCompletionSource
var tcs = new TaskCompletionSource();
var task = tcs.Task;
tcs.SetCancelled();
Other than that you can only cancel a running Task with a CancellationToken
You only need to wrap the calling code in a try/catch block where you're asking for the result, or waiting for the task to complete - those are the situations in which the exception is thrown. The code creating the task won't throw that exception, for example.
It's not clear what the alternative would be - for example:
string x = await GetTaskReturningString();
Here we never have a variable referring to the task, so we can't explicitly check the status. We'd have to use:
var task = GetTaskReturningString();
string x = await task;
if (task.Status == TaskStatus.Canceled)
{
...
}
... which is not only less convenient, but also moves the handling of the "something happened" code into the middle of the normal success path.
Additionally, by handling cancellation with an exception, if you have several operations, you can put all the handling in one catch block instead of checking each task separately:
try
{
var x = await GetFirstTask();
var y = await GetSecondTask(x);
}
catch (OperationCanceledException e)
{
// We don't care which was canceled
}
The same argument applies for handling cancellation in one place wherever in the stack the first cancellation occurred - if you have a deep stack of async methods, cancellation in the deepest method will result in the top-most task being canceled, just like normal exception propagation.

What happens when you await a failed task

I have a theoretical question to you. What happens if I await the Result of a Task inside another task? I want to know if my current system will work afterwards.
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
Can I avoid this deadlock somehow (without changing the system itself - task inside task)?
A task gets launched and does some stuff. At some point that task might need another one in order to process data which the current task is not able to process by itself. So I use await to ensure that the current task won't continue as long as he has not the result of the helper task. But what happens if the helper fails? Will the current task remain locked?
The core idea behind async and await is that asynchronous code works just about the same as synchronous code.
So, if you have synchronous code like this:
void HelperMethod()
{
throw new InvalidOperationException("test");
}
void DoStuff()
{
HelperMethod();
}
then you would expect DoStuff to propagate the InvalidOperationException from the helper method. Similarly, that's what happens with asynchronous code:
async Task HelperMethodAsync()
{
throw new InvalidOperationException("test");
}
async Task DoStuffAsync()
{
await HelperMethodAsync();
}
That is, DoStuffAsync will also propagate the InvalidOperationException.
Now, it doesn't work exactly the same way, of course, since it must be asynchronous, but the general idea is that all your control flow such as try/catch, for loops, etc, all "just work" for asynchronous code very similarly to synchronous code.
What's actually going on is that when HelperMethod ends with an InvalidOperationException, the exception is caught and placed on the returned Task, and the task is completed. When the await in DoStuffAsync sees that the task has completed, it examines its exceptions and re-raises the first one (in this case, there is only one, the InvalidOperationException). And it re-raises it in a way that preserves the call stack on the exception. This in turn causes the Task returned from DoStuffAsync to be completed with that same exception.
So, under the covers async and await are doing a bit of work to ensure that you can just call other methods with await and use try/catch the same way as you would in synchronous code. But most of the time you don't have to be aware of that.
It's really easy to test. For example:
[TestMethod, ExpectedException(typeof(Exception))]
public async Task DoFaultedTaskThrowOnAwait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
await task;
}
[TestMethod, ExpectedException(typeof(AggregateException))]
public void DoFaultedTaskThrowOnWait()
{
var task = Task.Factory.StartNew(() => { throw new Exception("Error 42"); });
task.Wait();
}
Both tests pass, notice that Wait throws an AggregateException, and await throws the Exception.
What happens if I await the Result of a Task inside another task?
You can only await a Task.Result if it is an awaitable (meaning it has a GetAwaiter method). This is rarely the case. I assume you mean await on an inner Task.
But what happens if the helper fails? Will the current task remain locked?
First, im not sure what you mean by "locked". The task isn't locked while you await on the inner task. Control is yielded back to the calling method until that inner task completes. If that inner task fails and you fail to properly handle that exception, you parent task will fault as well. You need to make sure you handle exceptions gracefully:
var task =
Task.Run(async () =>
{
try
{
await AsyncHelper.DoSomethingAsync();
}
catch (Exception e)
{
// Handle exception gracefully
}
});
If you await on the parent Task, you'll notice the inner exception propogating from the unhandled inner Task.
Can I avoid this deadlock somehow?
I see no reason why your code should deadlock in this specific case. Not sure why this worries you.

Why doesn't this exception get thrown?

I use a set of tasks at times, and in order to make sure they are all awaited I use this approach:
public async Task ReleaseAsync(params Task[] TaskArray)
{
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any()) tasks.Remove(await Task.WhenAny(tasks));
}
and then call it like this:
await ReleaseAsync(task1, task2, task3);
//or
await ReleaseAsync(tasks.ToArray());
However, recently I have been noticing some strange behavior and set to see if there was a problem with the ReleaseAsync method. I managed to narrow it down to this simple demo, it runs in linqpad if you include System.Threading.Tasks. It will also work slightly modified in a console app or in an asp.net mvc controller.
async void Main()
{
Task[] TaskArray = new Task[]{run()};
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any<Task>()) tasks.Remove(await Task.WhenAny(tasks));
}
public async Task<int> run()
{
return await Task.Run(() => {
Console.WriteLine("started");
throw new Exception("broke");
Console.WriteLine("complete");
return 5;
});
}
What I don't understand is why the Exception never shows up anywhere. I would have figured that if the Tasks with the exception were awaited, it would throw. I was able to confirm this by replacing the while loop with a simple for each like this:
foreach( var task in TaskArray )
{
await task;//this will throw the exception properly
}
My question is, why doesn't the shown example throw the exception properly (it never shows up anywhere).
TL;DR: run() throws the exception, but you're awaiting WhenAny(), which doesn't throw an exception itself.
The MSDN documentation for WhenAny states:
The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state.
Essentially what is happening is that the task returned by WhenAny simply swallows the faulted task. It only cares about the fact that the task is finished, not that it has successfully completed. When you await the task, it simply completes without error, because it is the internal task that has faulted, and not the one you're awaiting.
A Task not being awaited or not using its Wait() or Result() method, will swallow the exception by default. This behavior can be modified back to the way it was done in .NET 4.0 by crashing the running process once the Task was GC'd. You can set it in your app.config as follows:
<configuration>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
A quote from this blog post by the Parallel Programming team in Microsoft:
Those of you familiar with Tasks in .NET 4 will know that the TPL has the notion of “unobserved” exceptions. This is a compromise between two competing design goals in TPL: to support marshaling unhandled exceptions from the asynchronous operation to the code that consumes its completion/output, and to follow standard .NET exception escalation policies for exceptions not handled by the application’s code. Ever since .NET 2.0, exceptions that go unhandled on newly created threads, in ThreadPool work items, and the like all result in the default exception escalation behavior, which is for the process to crash. This is typically desirable, as exceptions indicate something has gone wrong, and crashing helps developers to immediately identify that the application has entered an unreliable state. Ideally, tasks would follow this same behavior. However, tasks are used to represent asynchronous operations with which code later joins, and if those asynchronous operations incur exceptions, those exceptions should be marshaled over to where the joining code is running and consuming the results of the asynchronous operation. That inherently means that TPL needs to backstop these exceptions and hold on to them until such time that they can be thrown again when the consuming code accesses the task. Since that prevents the default escalation policy, .NET 4 applied the notion of “unobserved” exceptions to complement the notion of “unhandled” exceptions. An “unobserved” exception is one that’s stored into the task but then never looked at in any way by the consuming code. There are many ways of observing the exception, including Wait()’ing on the Task, accessing a Task’s Result, looking at the Task’s Exception property, and so on. If code never observes a Task’s exception, then when the Task goes away, the TaskScheduler.UnobservedTaskException gets raised, giving the application one more opportunity to “observe” the exception. And if the exception still remains unobserved, the exception escalation policy is then enabled by the exception going unhandled on the finalizer thread.
From the comment:
these [tasks] were tied to managed resources and I wanted to release them when
they became available instead of waiting for all of them to complete
and then releasing.
Using a helper async void method may give you the desired behavior for both removing the finished tasks from the list and immediately throwing unobserved exceptions:
public static class TaskExt
{
public static async void Observe<TResult>(Task<TResult> task)
{
await task;
}
public static async Task<TResult> WithObservation(Task<TResult> task)
{
try
{
return await task;
}
catch (Exception ex)
{
// Handle ex
// ...
// Or, observe and re-throw
task.Observe(); // do this if you want to throw immediately
throw;
}
}
}
Then your code might look like this (untested):
async void Main()
{
Task[] TaskArray = new Task[] { run().WithObservation() };
var tasks = new HashSet<Task>(TaskArray);
while (tasks.Any<Task>()) tasks.Remove(await Task.WhenAny(tasks));
}
.Observe() will re-throw the task's exception immediately "out-of-band", using SynchronizationContext.Post if the calling thread has a synchronization context, or using ThreadPool.QueueUserWorkItem otherwise. You can handle such "out-of-band" exceptions with AppDomain.CurrentDomain.UnhandledException).
I described this in more details here:
TAP global exception handler

Exception in task not breaking immediately

Some pseudo code to illustrate my problem:
public async Task DoSomethingAsync()
{
try
{
var task1 = DoThisAsync(); // throws exception
var task2 = DoThatAsync();
await task1.Then(t => Handle(t));
await task2.Then(t => Handle(t));
}
catch (AggregateException)
{
Console.WriteLine("Whatnow?");
}
}
And Then is defined as such:
// from https://gist.github.com/rizal-almashoor/2818038
public static Task Then(this Task task, Action<Task> next)
{
var tcs = new TaskCompletionSource<AsyncVoid>();
task.ContinueWith(t=>
{
if (t.IsFaulted)
tcs.TrySetException(t.Exception); // continuing task1 this line only gets hit
// after DoThatAsync() is completed??
else
{
try
{
next(t);
tcs.TrySetResult(default(AsyncVoid));
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
});
return tcs.Task;
}
So my problem is that for some reason, even though DoThisAsync() throws an exception pretty early, I don't see "whatnow" until DoThatAsync() is finished.
This is not the exact code, I tried to simplify to not waste your time. If there's nothing here that explains this behavior let me know and I will add more detail.
Edit
For the purpose of this question we can imagine DoThisAsync() and DoThatAsync() are to asynchronouse methods that basically do the following:
DoThisAsync:
Thread.Sleep(30000); // wait a short perioud of time
throw new Exception(); // and throw an exception
DoThatAsnyc:
Thread.Sleep(240000); // wait a long period of time
Presumably your DoThisAsync starts a new task and the action of that task is what throws the exception--is that right?
In that case, the exception is stored within the Task. The exception will not be rethrown unless you call a trigger method like .Wait, or .Result. When you await the task returned from Then, it is causing that task's exception to be rethrown.
Edit:
Based on your edits showing the DoThisAsync:
When an async marked method that returns a Task causes an exception, that exception is stored in the Task (rather than allowing it to propagate). If you were to remove the async keyword I would expect the exception to happen at the time DoThisAsync is called.
Edit:
From Stephen Toub's Async/Await FAQ: http://blogs.msdn.com/b/pfxteam/archive/2012/04/12/async-await-faq.aspx:
What does the “async” keyword do when applied to a method?
When you mark a method with the “async” keyword, you’re really telling the compiler two things:
You’re telling the compiler that you want to be able to use the “await” keyword inside the method (you can use the await keyword if and only if the method or lambda it’s in is marked as async). In doing so, you’re telling the compiler to compile the method using a state machine, such that the method will be able to suspend and then resume asynchronously at await points.
You’re telling the compiler to “lift” the result of the method or any exceptions that may occur into the return type. For a method that returns Task or Task, this means that any returned value or exception that goes unhandled within the method is stored into the result task. For a method that returns void, this means that any exceptions are propagated to the caller’s context via whatever “SynchronizationContext” was current at the time of the method’s initial invocation.

How do I catch in C# an exception from an asynchronous method that is awaited?

I'm basically wondering how I should, in C#, catch exceptions from asynchronous methods that are waited on through the await keyword. Consider for example the following small console program, which most importantly contains a method called AwaitSync. AwaitSync calls TestAsync, which returns a Task that when executed throws an exception. I try to catch the exception in AwaitAsync, but it goes unhandled.
class Program
{
static void Main(string[] args)
{
AwaitAsync();
Console.ReadKey();
}
static async Task AwaitAsync()
{
try
{
await TestAsync();
}
catch (Exception)
{
Console.WriteLine("Exception caught");
}
}
static Task TestAsync()
{
return Task.Factory.StartNew(() => { throw new Exception("Test"); });
}
}
How am I supposed to go about catching the exception from the Task returned by TestAsync? While this example is a console program, my real life problem is within the context of of ASP.NET MVC / Web API.
EDIT:
Turns out the exception is getting caught, for technical reasons I just didn't notice the 'Exception caught' message before the terminal closed. In any case, Jon Skeet's answer was very valuable to my understanding of await and exception handling.
The code generated for the await expression will call GetResult() on the TaskAwaiter associated with the Task returned by TestAsync.
GetResult will notice that the task is faulted, fetch the first exception from the AggregateException within the task, and throw that.
The net result is that your catch block will catch the exception thrown in your task - but if you await a task which has multiple exceptions, you'll only see the first one unless you take special actions (there are various approaches to this).
As you're claiming the exception actually goes unhandled, it seems there's something in your code other than what you're showing - as the code you've given should certainly work, and does for me.

Categories

Resources