Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ignore unobserved exceptions with async/await in MonoTouch?

In previous versions of MonoTouch, I used to do this to ignore unobserved exceptions:

TaskScheduler.UnobservedTaskException += delegate(object sender, UnobservedTaskExceptionEventArgs e) {
    Console.WriteLine (e);
    e.SetObserved ();
};

Whether it is a good practice is debatable but I'd like to know to achieve the same effect with async/await keywords now officially supported in Xamarin.iOS 6.4.

Here is the code I use for testing:

async void OnClick (object sender, EventArgs e)
{
    await Task.Run (() => { throw new Exception (); });
}

When I run it, debugger pauses in AsyncVoidMethodBuilder:

enter image description here

I read that .NET 4.5 supposedly changed the behaviour so unobserved exceptions don't crash the app—but this doesn't help if exceptions are posted to UIKit synchronisation context where I can't handle them.

Is there a way to ignore unobserved exceptions from await in MonoTouch?

like image 287
Dan Abramov Avatar asked Jul 25 '13 12:07

Dan Abramov


1 Answers

This is the correct behavior of async void methods: they are supposed to raise the exception on the SynchronizationContext that was active at the time the async void method started.

The change you mentioned in .NET 4.5 is dealing only with unobserved task exceptions, and does not apply to async void methods.

In the (Microsoft) .NET world, different SynchronizationContext implementations have different top-level error handling. WPF, WinForms, and ASP.NET all have different ways of handling that error, usually as part of an Application type.

I looked through Mono's UIKit API - though I'm not a regular Mono user - and couldn't find any top-level error handling in UIApplication, and UIKitSynchronizationContext looks like it's not public (or at least not documented).

Another way of looking at this problem: the exception handling behavior for async void methods is designed to be just like event handlers would have (for more info, see my MSDN article). So you can answer the question with another question: in UIKit, how would you handle this exception?

void OnClick (object sender, EventArgs e)
{
  throw new Exception();
}

You would handle your async void exception in exactly the same way.

Alternatively, if you want to keep using UnobservedTaskException, you can simply not observe the task exception (in your async void code, Task.Run returns a task that gets an exception, and you're observing it by using await):

void OnClick (object sender, EventArgs e)
{
  Task.Run(() => { throw new Exception(); });
}

However, I recommend using async void for event handlers and (eventually) awaiting all your tasks. This will ensure you aren't getting any "silent errors" (ignored task exceptions) where your program just stops working correctly and you don't know why.

like image 144
Stephen Cleary Avatar answered Oct 20 '22 15:10

Stephen Cleary