Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting a Task's unexpected Exception to bring down the application earlier than Garbage Collection

Typically, for code that I don't expect to throw exceptions but does (i.e. a programming error), I want my application to crash (so that it doesn't corrupt data, report invalid data to the user, etc.).

Is there a best practice for getting (closer to) this behavior when using Tasks? We've registered a handler for TaskScheduler.UnobservedTaskException. The problem is that this can occur much later than the causing unexpected exception.

Question: Which option should I use if any:

  1. Should I wrap my Tasks action in a try/catch and escalate in the catch for exceptions I don't expect? And if so, what should I do to escalate (i.e. I'd like to get it to fire the AppDomain.UnhandledException event and terminate.

  2. Should I attach a continuation (OnlyOnFaulted) on the ui thread (this is a Winforms application) that rethrows the exception if it is not an expected exception?

  3. Is there a better or more standard approach?

Here's what #1 might look like:

var t1 = Task.Factory.StartNew(() =>
    {
        try
        {
            string path = null; // Programming error.  Should have been a valid string.  Will cause System.ArgumentNullException below
            using (FileStream fs = File.Create(path))
            {

            }
        }
        catch (System.IO.IOException) { throw; } // Expected possible exception
        catch (System.UnauthorizedAccessException) { throw; }
        catch
        {
            // Anything caught here is not an expected exception and should be escalated.
            // But how?
        }
    });

Here's what #2 might look like:

TaskScheduler uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
var t1 = Task.Factory.StartNew(() =>
    {
        string path = null; // Programming error.  Should have been a valid string.  Will cause System.ArgumentNullException below
        using (FileStream fs = File.Create(path))
        {

        }
    });

t1.ContinueWith(t =>
    {
        Exception ex = t.Exception;
        if (ex is IOException || ex is UnauthorizedAccessException) // Expected exceptions (do nothing)
            return;

        throw ex; // Not expected (escalate by rethrowing)

    }, CancellationToken.None, TaskContinuationOptions.OnlyOnFaulted, uiTaskScheduler);
like image 726
Matt Smith Avatar asked Nov 03 '22 21:11

Matt Smith


1 Answers

Attaching a continuation feels like a good approach to me. If you're comfortable with the assumption that you won't be blocking the UI thread for too long for other reasons, forcing the continuation to run on the UI thread seems like a very reasonable option to me. That way you can perform any UI tasks you need to as well, as part of the emergency shutdown.

like image 164
Jon Skeet Avatar answered Nov 15 '22 06:11

Jon Skeet