Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best strategy for finding tasks with unobserved exceptions

Suppose I have the following code:

private static void Run()
{
    TaskScheduler.UnobservedTaskException += delegate { Console.WriteLine("Unobserved task exception!"); };

    try
    {
        RunAsync().Wait();
    }
    catch (Exception)
    {
    }

    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    Thread.Sleep(Timeout.InfiniteTimeSpan);
}

private static async Task RunAsync()
{
    var task1 = ThrowAsync();
    var task2 = ThrowAsync();

    await task1;
    await task2;
}

private static async Task ThrowAsync()
{
    throw new Exception("Test exception");
}

This code outputs Unobserved task exception! because the exception in task2 is not observed.

My question is the following: is there any way to programmatically determine which task had an unobserved exception? For example, I want to get a stack trace for a method which invoked a task or something like this:

Unobserved exception: task2 in RunAsync()

Sadly, exception stack trace is not enough. The code above is just a demonstration, in a real-world application I sometimes have unobserved task exceptions with a stack trace like this:

System.AggregateException: 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. ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Web.HttpApplication.get_CurrentModuleContainer()
   at System.Web.HttpApplication.PipelineStepManager.ResumeSteps(Exception error)
like image 753
RX_DID_RX Avatar asked Dec 03 '15 12:12

RX_DID_RX


1 Answers

One would be able to tell which Task instance had its exception unobserved. That would be task2 under the second await

await task1;
await task2;

because the first await "observes" the result of task1.

Then if the event handler is modified to presenting the actual Task exception that TaskScheduler.UnobservedTaskException provides

    TaskScheduler.UnobservedTaskException += delegate(object o, UnobservedTaskExceptionEventArgs ea)
    {
        Console.WriteLine($"Unobserved task exception! {ea.Exception.ToString()}");
    };

then the failing region of the Task code can be tracked down by observing the exception's stack trace:

Unobserved task exception!
System.AggregateException: 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. (Test exception)
---> System.Exception: Test exception
   at ConsoleApp1.Program.ThrowAsync() in C:\Users\sergepavlov\Desktop\ConsoleApp1\ConsoleApp1\Program.cs:line 42
   --- End of inner exception stack trace ---
---> (Inner Exception #0) System.Exception: Test exception
   at ConsoleApp1.Program.ThrowAsync() in C:\Users\sergepavlov\Desktop\ConsoleApp1\ConsoleApp1\Program.cs:line 42
<--- 

Hope that makes sense.

like image 143
Serge Pavlov Avatar answered Nov 15 '22 00:11

Serge Pavlov