I can hookup to AppDomain.CurrentDomain.UnhandledException
to log exceptions from background threads, but how do I prevent them terminating the runtime?
First, you really should try not to have exceptions thrown - and not handled - in a background thread. If you control the way your delegate is run, encapsulate it in a try catch block and figure a way to pass the exception information back to your main thread (using EndInvoke if you explicitly called BeginInvoke, or by updating some shared state somewhere).
Ignoring a unhandled exception can be dangerous. If you have a real un-handlable exception (OutOfMemoryException comes into mind), there's not much you can do anyway and your process is basically doomed.
Back to .Net 1.1, an unhandled exception in a backgroundthread would just be thrown to nowhere and the main thread would gladly plough on. And that could have nasty repercussions. So in .Net 2.0 this behavior has changed.
Now, an unhandled exception thrown in a thread which is not the main thread will terminate the process. You may be notified of this (by subscribing to the event on the AppDomain) but the process will die nonetheless.
Since this can be inconvenient (when you don't know what will be run in the thread and you are not absolutely sure it's properly guarded, and your main thread must be resilient), there's a workaround. It's intended as a legacy settings (meaning, it's strongly suggested you make sure you don't have stray threads) but you can force the former behavior this way :
Just add this setting to your service/application/whatever configuration file :
<configuration>
<runtime>
<!-- the following setting prevents the host from closing when an unhandled exception is thrown -->
<legacyUnhandledExceptionPolicy enabled="1" />
</runtime>
</configuration>
It doesn't seem to work with ASP.NET, though.
For more information (and a huge warning that this setting may not be supported in upcoming versions of the CLR) see http://msdn.microsoft.com/en-us/library/ms228965.aspx
Keeping the answer short, yes, you do can prevent the runtime from terminating.
Here is a demo of the workaround:
class Program
{
void Run()
{
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
Console.WriteLine("Press enter to exit.");
do
{
(new Thread(delegate()
{
throw new ArgumentException("ha-ha");
})).Start();
} while (Console.ReadLine().Trim().ToLowerInvariant() == "x");
Console.WriteLine("last good-bye");
}
int r = 0;
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Interlocked.Increment(ref r);
Console.WriteLine("handled. {0}", r);
Console.WriteLine("Terminating " + e.IsTerminating.ToString());
Thread.CurrentThread.IsBackground = true;
Thread.CurrentThread.Name = "Dead thread";
while (true)
Thread.Sleep(TimeSpan.FromHours(1));
//Process.GetCurrentProcess().Kill();
}
static void Main(string[] args)
{
Console.WriteLine("...");
(new Program()).Run();
}
}
Essentially, you just did not let the runtime to show the "... program has stopped working" dialog.
In case you need to log the exception and silently exit, you can call Process.GetCurrentProcess().Kill();
AppDomain.CurrentDomain.UnhandledException += (sender, e2) =>
{
Thread.CurrentThread.Join();
};
But be careful, this code will freeze all the stack memory of the Thread and thread's managed object itself. However, if your application is in determined state (may be you threw LimitedDemoFunctionalityException or OperationStopWithUserMessageException) and you are not developing 24/7 application this trick will work.
Finally, I think MS should allow developers to override the logic of unhandled exceptions from the top of the stack.
From Joe Albahari's excellent threading article:
The .NET framework provides a lower-level event for global exception handling: AppDomain.UnhandledException. This event fires when there's an unhandled exception in any thread, and in any type of application (with or without a user interface). However, while it offers a good last-resort mechanism for logging untrapped exceptions, it provides no means of preventing the application from shutting down – and no means to suppress the .NET unhandled exception dialog.
In production applications, explicit exception handling is required on all thread entry methods. One can cut the work by using a wrapper or helper class to perform the job, such as BackgroundWorker (discussed in Part 3).
Here is a great blog post about this problem: Handling "Unhandled Exceptions" in .NET 2.0
IMO it would be right to handle exceptions in background threads manually and re-throw them via callback if necessary.
delegate void ExceptionCallback(Exception ex);
void MyExceptionCallback(Exception ex)
{
throw ex; // Handle/re-throw if necessary
}
void BackgroundThreadProc(Object obj)
{
try
{
throw new Exception();
}
catch (Exception ex)
{
this.BeginInvoke(new ExceptionCallback(MyExceptionCallback), ex);
}
}
private void Test()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(BackgroundThreadProc));
}