Catching unhandled exceptions with asp.net webapi and TPL - c#

I have a scenario, where I create Tasks as a part of a Webapi call. When an exception happens in a task it does not get caught and I can't figure out how to implement a global exception handler for this.
In particular:
This question and the solution for Web API Global Error Handling does not work for Tasks. I'm guessing because these are executed on threads webapi has no awareness of.
Subscribing to AppDomain.UnhandledException Event does not work either. Possibly because web host catches all the unhandled exceptions and is not letting them bubble up.
If you believe that any of the above should work let me know, and I'll come up with a small reproducible example of what I'm observing. Otherwise, what is the right way of doing this, that would work?
[HttpGet]
public IQueryable<Thingies> Thingies()
{
Task.Run(() => { throw new ApplicationException("How do I catch all of these globally?"); });
return _db.Thingies;
}
I also should note that the application does not have dependencies on System.Web and I would not like to introduce them.

Exceptions that are thrown from inside a running task aren't unhandled. They are captured on that task turning it faulted.
The correct way to handle these exceptions would be to await each task, or register continuations using Task.ContinueWith. However, if you want a global handler you can use the TaskScheduler.UnobservedTaskException event.
When there's an exception inside a task it's handled and stored on the Task object for you to observe. If the task gets garbage collected and the exception wasn't yet observed by any code .Net knows that it will never be observed (as you don't have a reference to the task) and raises the TaskScheduler.UnobservedTaskException event (in .Net 4.0 it will also crash the app and that behavior can be replicated in newer versions as well).
So, an event handler for TaskScheduler.UnobservedTaskException will globally handle all exceptions raised from tasks, but only after they have been GCed (which isn't immediately):
TaskScheduler.UnobservedTaskException += (sender, args) => HandleException(args.Exception);

Use async-await instead of directly using Task, the exception handling will be straightforward as below:
try
{
// Asynchronous implementation.
await Task.Delay(1000);
}
catch (Exception ex)
{
// Handle exceptions.
}
Or, if you still need to use task then use continue with as described here: How to manage properly an exception in a Task with ContinueWith

Related

Exception caught in System.Threading.Timers TimerCallback event handler and then re-thrown not sent back to main thread

I need for exceptions thrown within a timed event to be bubbled up and handled outside of the event handler context. I had read that System.Threading.Timers would be able to do that, provided that, as outlined in the answer to this question, the exception is caught in the callback method and a mechanism is used to re-throw it. Using this example as a guide, I created an event handler which throws and a method which should catch and re-throw the exception using an IProgress object:
void ThrowerThreaded() {
var progress = new Progress<Exception>((ex) => {
throw ex;
});
Timer timer = new Timer(x => onTimerElapsedThrow2(progress), null, 10, -1);
Thread.Sleep(1000);
}
void onTimerElapsedThrow2(IProgress<Exception> progress) {
try {
throw new Exception();
} catch (Exception ex) {
progress.Report(ex);
}
}
I then wrote a unit test to see if the exception would bubble up:
[TestMethod]
public void TestThreadedTimerThrows() {
Assert.ThrowsException<Exception>(ThrowerThreaded);
}
The test case fails indicating no exception was thrown. If I debug the test case, I can clearly see that the exception is caught and re-thrown in ThrowerThreaded() however the method still continues and exists normally. Why is the exception still being suppressed?
I'm guessing ThrowerThreaded is running on a background thread. That means it does not have a synchronizationContext, since these are intended to synchronize UI applications. This means the callback is called on the threadpool:
Any handler provided to the constructor or event handlers registered with the ProgressChanged event are invoked through a SynchronizationContext instance captured when the instance is constructed. If there is no current SynchronizationContext at the time of construction, the callbacks will be invoked on the ThreadPool.
Rethrowing the exception on a threadpool thread will probably kill that thread, and I'm somewhat surprised it did not kill the application, but it's possible that such behavior is overridden by the testing framework you are using.
To solve this you really need to handle the exception in the callback instead of re-throwing it. If you are not handling the exception, who should? there is a unhandledExceptionEvent, but that is intended for logging before you close your app.
You could handle the exception taking a callback in ThrowerThreaded that you delegate the exception handling to. Another alternative would be to create a TaskCompletionSource that allow you to return a task, and set the task to 'failed' by calling SetException on the source.
It is also poor practice to re-throw the same exception object, since you will lose the call stack, you should instead wrap the exception in a new exception that is thrown.

Asynchronous error handling in MVC 5 filters

I've made a custom filter attribute extending the HandleErrorAttribute, that catches all exceptions.
My entire application is built around async architechture, throughout the stack.
This means, that I somehow have to use the OnException(ExceptionContext contect) override asynchronously. Something like this, would be how I imagined it (Simplified for readability):
public async override void OnException(ExceptionContext context)
{
base.OnException(context);
var logId = await LogException(e, httpContext); //LogException calls an async method and returns the ID of the exception, after it has been saved to the database. I this ID in the filterContext, which is why I need the result of the method
}
However, this would result in InvalidOperationException: An asynchronous operation cannot be started at this time. Asynchronous operations may only be started within an asynchronous handler or module or during certain events in the Page lifecycle.
I cannot simply call .Result on the task when called synchronously, as this would cause deadlocks.
So I guess my question is: How do I create a filter in MVC5, that can log errors asynchronously?
Since error handling usually is not hot code there are no performance concerns here. Therefore, do what's most convenient to you.
For example, you can block on the task returned from LogException. There are multiple ways you can reliably avoid a deadlock. A simple way is Task.Run(() => LogException()).Wait();. This works because the task body runs without a synchronization context.
You also can add ConfigureAwait(false) inside of LogException but you cannot miss a single place. That makes this brittle.
I suggest you to change approach and implement own ExceptionLogger because it's ExceptionLogger job to log exceptions
We provide two new user-replaceable services, IExceptionLogger and IExceptionHandler, to log and handle unhandled exceptions. The services are very similar, with two main differences:
We support registering multiple exception loggers but only a single exception handler.
Exception loggers always get called, even if we’re about to abort the connection. Exception handlers only get called when we’re still able to choose which response message to send.
Both services provide access to an exception context containing relevant information from the point where the exception was detected, particularly the HttpRequestMessage, the HttpRequestContext, the thrown exception and the exception source.
Here the sample code
public class ExceptionLogger : IExceptionLogger
{
public virtual Task LogAsync(ExceptionLoggerContext context,
CancellationToken cancellationToken)
{
return MyLogger.Log(context, cancellationToken);
}
}

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

Task unhandled exceptions

I'm trying to understand what is going on with exceptions that are thrown within a task object and never handled.
On MSDN it said that:
If you do not wait on a task that propagates an exception, or access
its Exception property, the exception is escalated according to the
.NET exception policy when the task is garbage-collected.
So I don't quite understand in what way those exceptions affect program flow. I thought that those exceptions should interrupt execution as soon as they are garbage-collected. But I can not design this behaviour. Within the following snippet the thrown exception doesn't show up.
// Do something ...
Task.Run (()=> {throw new Exception("Exception in the task!");});
// Do something else
Please, can anyone explain how unhandled task exception are dealt with and how they affect program flow.
You're describing the behavior as it was in .NET 4, but it will be difficult for you to force the garbage collection and actually observe that behavior. The following quote from Stephen Toub's excelent write-up on the subject should make it even more clear:
Tasks keep track of whether an unhandled exception has been
“observed.” In this context, “observed” means that code has joined
with the Task in some fashion in order to at least be made aware of
the exception. This could be calling Wait/WaitAll on the Task. It
could be checking the Task’s Exception property after the Task has
completed. Or it could be using a Task’s Result property.
If a Task sees that its exception has been observed in some manner,
life is good. If, however, all references to a Task are removed
(making the Task available for garbage collection), and if its
exception hasn’t yet been observed, the Task knows that its exception
will never be observed. In such a case, the Task takes advantage of
finalization, and uses a helper object to propagate the unhandled
exception on the finalizer thread. With the behavior described
earlier, that exception on the finalizer thread will go unhandled and
invoke the default unhandled exception logic, which is to log the
issue and crash the process.
He also suggested two useful extension methods for handling exceptions in "fire-and-forget" tasks: one ignoring the exception and the other one immediately crashing the process:
public static Task IgnoreExceptions(this Task task)
{
task.ContinueWith(c => { var ignored = c.Exception; },
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DetachedFromParent);
return task;
}
public static Task FailFastOnException(this Task task)
{
task.ContinueWith(c => Environment.FailFast(“Task faulted”, c.Exception),
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously |
TaskContinuationOptions.DetachedFromParent);
return task;
}
In .NET 4.5 the default behavior has changed. Again, a quote from another Stephen Toub's post on the subject (thanks to mike z for bringing it to my attention in the comments):
To make it easier for developers to write asynchronous code based on
Tasks, .NET 4.5 changes the default exception behavior for unobserved
exceptions. While unobserved exceptions will still cause the
UnobservedTaskException event to be raised (not doing so would be a
breaking change), the process will not crash by default. Rather, the
exception will end up getting eaten after the event is raised,
regardless of whether an event handler observes the exception. This
behavior can be configured, though.
Please note the code above is not quite correct. You must return the pointer to the task.ContinueWith, not the passed-in task:
public static Task IgnoreExceptions(this Task task)
{
var t = task.ContinueWith(c => { var ignored = c.Exception; },
TaskContinuationOptions.OnlyOnFaulted |
TaskContinuationOptions.ExecuteSynchronously);
return t;
}
EDIT:
This is challenging because it depends on how you chain your calls together. For example, the following call doesn't work the way I would expect:
public Task MyServiceCall()
{
return Task.Run(() => DoSomething()).IgnoreExceptions();
}
This method does indeed throw exceptions because the the accepted answer returns the initial task (not the one which observes the exception). This could be problematic with other calls, such as .Wait, .WhenAll etc. One might think that the task will never throw, but it can.
However, my suggested change would break the following:
public void SomeMethod()
{
var myTask = new Task(() => ...);
myTask.IgnoreExceptions().Start();
}
Internally we've decided to obsolete this extension method as it's too confusing!

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

What does this mean and how to resolve it?
I am using TPL tasks.
The whole error
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.
at System.Threading.Tasks.TaskExceptionHolder.Finalize()
mscorlib
If you create a Task, and you don't ever call task.Wait() or try to retrieve the result of a Task<T>, when the task is collected by the garbage collector, it will tear down your application during finalization. For details, see MSDN's page on Exception Handling in the TPL.
The best option here is to "handle" the exception. This can be done via a continuation - you can attach a continuation to the task, and log/swallow/etc the exception that occurs. This provides a clean way to log task exceptions, and can be written as a simple extension method, ie:
public static void LogExceptions(this Task task)
{
task.ContinueWith( t =>
{
var aggException = t.Exception.Flatten();
foreach(var exception in aggException.InnerExceptions)
LogException(exception);
},
TaskContinuationOptions.OnlyOnFaulted);
}
With the above, you can prevent any task from tearing down the app, and logging it, via:
Task.Factory.StartNew( () =>
{
// Do your work...
}).LogExceptions();
Alternatively, you can subscribe to the TaskScheduler.UnobservedTaskException and handle it there.
Sure; it means a Task got finalized after being left to garbage collection, but the task itself failed. There are two fixes:
handle the tasks fail directly (use ContinueWith(...) to subscribe, and check .IsFaulted and .Exception on the Task in the parameter)
handle the TaskScheduler.UnobservedTaskException event, and mark it observed (call e.SetObserved() after logging the error)

Categories

Resources