4

The following code, when run in a netcoreapp2.0 application, doesn't seem to end up throwing the UnobservedTaskException

using System;
using System.Threading;
using System.Threading.Tasks;

namespace ConsoleApp3 {

    class Program {

        public static void Main(string[] args) {

            TaskScheduler.UnobservedTaskException += (s, e) => {
                /// Try to crash the application - we wanna nail unobserved exceptions dead.
                /// Unfortunately, this code never seems to run.
                Console.WriteLine("UnobservedTaskException thrown.");
                throw e.Exception; 
            };

            var t = Task.Run(() => {
                throw new NotImplementedException();
            });

            while (!t.IsFaulted)
                Thread.Sleep(1);

            t = null;
            Console.WriteLine("Task is faulted.");

            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.ReadKey();
        }

    }
}

Here's the project file. How can I get the UnobservedTaskException handler to be fired?

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
    <LangVersion>7.3</LangVersion>
  </PropertyGroup>

</Project>

In other stackoverflow posts, I've seen advice to use the following snippet in the project file, but it only works for .net framework 4.0+ projects. If there's an equivalent for the .netcore project file, that might be what I need to know.

<runtime> 
    <ThrowUnobservedTaskExceptions enabled="true"/> 
</runtime> 
bboyle1234
  • 4,859
  • 2
  • 24
  • 29

2 Answers2

5

I had the same issue. Just try it run in RELEASE mode. I tested it and it's working with console application .net core version 2.2.

internal class Program
{
    private static void Main(string[] args)
    {
        // REMEMBER TO RUN IN RELEASE MODE

        var handler = new EventHandler<UnobservedTaskExceptionEventArgs>(Unobserved);
        TaskScheduler.UnobservedTaskException += handler;

        Task.Run(() => { Console.WriteLine("task 1"); throw new Exception("TASK 1 EXCEPTION"); });
        Task.Run(() => { Console.WriteLine("task 2"); throw new Exception("TASK 2 EXCEPTION"); });

        Thread.Sleep(1000);

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

        Thread.Sleep(1000);

        Console.ReadKey();
    }

    private static void Unobserved(object o, UnobservedTaskExceptionEventArgs e)
    {
        e.SetObserved(); // optional
        foreach (var ex in e.Exception.InnerExceptions)
        {
            Console.WriteLine(ex.Message);
        }
    }
}
abinkows
  • 66
  • 1
  • 3
  • 1
    You don't as a rule have to run in release mode. If you move the task starting to a helper method, you'll see the unobserved exception fire in your debug build as well. – Eric J. Oct 19 '19 at 03:25
2

No answers yet, but I find it a bit intriguing. I'm looking at the source code on GitHub. The only place the event handler is called is from the PublishUnobservedTaskException method in the TaskScheduler class.

The only place PublishUnobservedTaskException is called is from the finalizer of TaskExceptionHolder. However, there are a few conditions that have to be met before it will call PublishUnobservedTaskException, as you can see in that source code.

I've enabled debugging the .NET source code, but I'm getting a different version of TaskExceptionHolder than what's on GitHub and it's clearly not matching up with the breakpoints I'm trying to make. So I can't confirm exactly why it's not getting called, and I've run out of time this evening.

It might even be worth creating an issue for this over at GitHub.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • 1
    In debug mode, the task isn't garbage collected until AFTER the entire method has completed. So you can see values of variables in the debugger. In Release mode, the variables are aggressively collected, before the method ends. Hence the accepted answer works. – bboyle1234 Jun 29 '21 at 07:44