Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to catch/observe an unhandled exception thrown from a Task

I'm trying to log / report all unhandled exceptions in my app (error reporting solution). I've come across a scenario that is always unhandled. I'm wondering how would I catch this error in an unhandled manner. Please note that I've done a ton of research this morning and tried a lot of things.. Yes, I've seen this, this and many more. I'm just looking for a generic solution to log unhandled exceptions.

I have the following code inside of a console test apps main method:

Task.Factory.StartNew(TryExecute);

or

Task.Run((Action)TryExecute);

as well as the following method:

private static void TryExecute() {
   throw new Exception("I'm never caught");
}

I'm already tried wiring up to the following in my app, but they are never called.

AppDomain.CurrentDomain.UnhandledException
TaskScheduler.UnobservedTaskException

In my Wpf app where I initially found this error I also wired up to these events but it was never called.

Dispatcher.UnhandledException
Application.Current.DispatcherUnhandledException
System.Windows.Forms.Application.ThreadException

The only handler that is called ever is:

AppDomain.CurrentDomain.FirstChanceException

but this is not a valid solution as I only want to report uncaught exceptions (not every exception as FirstChanceException is called before any catch blocks are ever executed / resolved.

like image 688
Blake Niemyjski Avatar asked Oct 03 '13 16:10

Blake Niemyjski


1 Answers

The TaskScheduler.UnobservedTaskException event should give you what you want, as you stated above. What makes you think that it is not getting fired?

Exceptions are caught by the task and then re-thrown, but not immediately, in specific situations. Exceptions from tasks are re-thrown in several ways (off the top of my head, there are probably more).

  1. When you try and access the result (Task.Result)
  2. Calling Wait(), Task.WaitOne(), Task.WaitAll() or another related Wait method on the task.
  3. When you try to dispose the Task without explicitly looking at or handling the exception

If you do any of the above, the exception will be rethrown on whatever thread that code is running on, and the event will not be called since you will be observing the exception. If you don't have the code inside of a try {} catch {}, you will fire the AppDomain.CurrentDomain.UnhandledException, which sounds like what might be happening.

The other way the exception is re-thrown would be:

  • When you do none of the above so that the task still views the exception as unobserved and the Task is getting finalized. It is thrown as a last ditch effort to let you know there was an exception that you didn't see.

If this is the case and since the finalizer is non-deterministic, are you waiting for a GC to happen so that those tasks with unobserved exceptions are put in the finalizer queue, and then waiting again for them to be finalized?

EDIT: This article talks a little bit about this. And this article talks about why the event exists, which might give you insight into how it can be used properly.

like image 197
Christopher Currens Avatar answered Sep 19 '22 00:09

Christopher Currens