Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnobservedTaskException does not catch exceptions from async/await code #57

Closed
thomaseyde opened this issue Aug 28, 2015 · 2 comments
Closed

Comments

@thomaseyde
Copy link

Here's a naive unit test demonstrating that exceptions happening inside of async/await code is not caught:

[TestFixture]
public class Unobserved_task_exceptions
{
    [Test]
    public void Async_exceptions_not_caught()
    {
        var errors = new List<string>();

        TaskManager.UnobservedTaskException += (sender, args) => { errors.Add(args.ExceptionObject.ToString()); };
        TaskManager.Initialize(new FaultySchedule());
        Thread.Sleep(1500);

        Assert.IsNotEmpty(errors);
    }

    public class FaultySchedule : Registry
    {
        public FaultySchedule()
        {
            Schedule(async () => { await new FaultyComponent().Run(); }).ToRunOnceIn(1).Seconds();
        }
    }

    public class FaultyComponent
    {
        public async Task Run()
        {
            await Task.Delay(0);

            throw new Exception("Expected!");
        }
    }
}
@thomaseyde
Copy link
Author

After some investigation, I found this event handler to do the trick:

AppDomain.CurrentDomain.UnhandledException

I'm not sure if this handler catches all exceptions, or if we need to use TaskManager.UnobservedTaskException as well.

@tallesl
Copy link
Contributor

tallesl commented Mar 18, 2016

When you () => { } the library creates a thread and calls your action on it. If any exception is raised the library is able to catch it and tell you on UnobservedTaskException.

When you async () => { } the library creates a thread that then calls your action which, for being asynchronous, spawns another thread. Since the library don't await your action (it just calls it), the unhandled exception in this second thread crashes the application and you're only able to get a last glance at it on UnhandledException of AppDomain before everything falls down.

Here's a little illustration of what's going on:

static async Task Boom()
{
    throw new Exception("BOOOM!");
}

static void Main(string[] args)
{
    // Bye-bye message
    AppDomain.CurrentDomain.UnhandledException += (sender, e) => Console.WriteLine("Aww, we crashed...");

    Action synchronous = () => Boom().Wait();
    Action asynchronous = async () => await Boom();

    // This one prints "Gotcha!"
    try { synchronous(); }
    catch { Console.WriteLine("Gotcha!"); }

    // This one doesn't print "Gotcha!" and crashes the application
    try { asynchronous(); }
    catch { Console.WriteLine("Gotcha!"); }

    Thread.Sleep(Timeout.Infinite);
}

But don't worry, fixing your example is rather simple:

Schedule(() => {
    var faulty= new FaultyComponent().Run(); // Here the faulty task starts to run on its own thread
    // Do some other stuff
    faulty.Wait(); // Here we join back the faulty task, if any exception were raised you're gonna get it wrapped in an AggregateException
}).ToRunOnceIn(1).Seconds();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants