Re: Exception handling in AppDomain threads

by Vadym Stetsiak on 11/2/2007 2:36:00 AM Hello, Marc!


With best regards, Vadym Stetsiak.
Blog: http://vadmyst.blogspot.com

You wrote on Thu, 1 Nov 2007 12:45:00 -0700:

 MS> Hi,

 MS> I'm trying to build a robust application framework that will be used
 MS> by other developers in the future. Their applications should be
 MS> able to recover themselves if they are crashing.

 MS> So, I thought, the best way to handle this is to lauch their stuff
 MS> in a separate AppDomain and when that AppDomain crashes, unload it
 MS> and restart it.
 MS> This seems to work fine, unless an unhandled exception occurs in a
 MS> background thread that is started from that new AppDomain. Instead
 MS> of just that AppDomain crashing, the complete application is
 MS> closed.

 MS> Does anyone know a way to prevent the application to close if an
 MS> unhandled exception occurs in a background thread that is started
 MS> from another
 MS> AppDomain?

 MS> Any help is welcome,

 MS> Marc

 MS> Below is a test application that demonstrates what goes wrong. The
 MS> goal is to keep the application running while changing nothing in
 MS> the Tester class, because that is the part that will be written by
 MS> other developers. I have no control over them if they do something
 MS> stupid like not handling any exceptions in background threads.

 MS> <code>
 MS> using System;

 MS> using System.Reflection;

 MS> using System.Threading;

 MS> using System.Windows.Forms;

 MS> namespace CrashRecoveryTest

 MS> {

 MS> internal class Program : MarshalByRefObject

 MS> {

 MS> private static void Main(string[] args)

 MS> {

 MS> try

 MS> {

 MS> Application.ThreadException += Application_ThreadException;

 MS> Application.SetUnhandledExceptionMode(UnhandledExceptionMode.
 MS> CatchException);

 MS> AppDomain.CurrentDomain.UnhandledException +=
 MS> CurrentDomain_UnhandledException;

 MS> Console.WriteLine("Starting new AppDomain");

 MS> for (int i = 0; i < 3; i++)

 MS> {

 MS> AppDomain newDomain = AppDomain.CreateDomain("TestDomain");

 MS> newDomain.UnhandledException += newDomain_UnhandledException;

 MS> Console.WriteLine("Loading Tester");

 MS> Tester tester =
 MS> (Tester)newDomain.CreateInstanceAndUnwrap(Assembly.
 MS> GetExecutingAssembly().GetName().Name, typeof(Tester).FullName);

 MS> Console.WriteLine("Calling Tester.Run method");

 MS> try

 MS> {

 MS> tester.Run();

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Caught exception " + ex.Message);

 MS> }

 MS> Console.WriteLine("Unloading AppDomain");

 MS> newDomain.UnhandledException -= newDomain_UnhandledException;

 MS> try

 MS> {

 MS> AppDomain.Unload(newDomain);

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Cannot unload AppDomain: " + ex.Message);

 MS> }

 MS> }

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Exception occurred: "+ex.Message );

 MS> }

 MS> Console.WriteLine("Application ended ok");

 MS> }

 MS> private static void Application_ThreadException(object sender,
 MS> ThreadExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Application.ThreadException occurred");

 MS> }

 MS> private static void CurrentDomain_UnhandledException(object sender,
 MS> UnhandledExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Unhandled exception occurred in default
 MS> AppDomain");

 MS> }

 MS> private static void newDomain_UnhandledException(object sender,
 MS> UnhandledExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Exception occurred in TestDomain");

 MS> }

 MS> }

 MS> public class Tester : MarshalByRefObject

 MS> {

 MS> public Tester()

 MS> {

 MS> Console.WriteLine("Tester is started in AppDomain " +
 MS> AppDomain.CurrentDomain.FriendlyName);

 MS> }

 MS> public void Run()

 MS> {

 MS> Console.WriteLine("Starting worker thread...");

 MS> Thread workerThread = new Thread(DoWork);

 MS> workerThread.Start();

 MS> Console.WriteLine("Doing some interesting work...");

 MS> for (int i = 0; i < 10; i++)

 MS> Thread.Sleep(1000);

 MS> workerThread.Join();

 MS> }

 MS> private static void DoWork()

 MS> {

 MS> Console.WriteLine("Worker thread is running in AppDomain "+
 MS> AppDomain.CurrentDomain.FriendlyName);

 MS> Thread.Sleep(3000);

 MS> Console.WriteLine("Worker thread about to throw an exception...");

 MS> throw new ApplicationException("Oops");

 MS> }

 MS> }

 MS> }

 MS> </code>


 

Re: Exception handling in AppDomain threads

by Vadym Stetsiak on 11/2/2007 2:45:00 AM Hello, Marc!

Ignore previous message...

The behavior you observe is by design (see
http://msdn2.microsoft.com/en-us/library/ms228965.aspx ).

If you want to recover from unhadled exceptions - your framework has to be
in control of every operation that 3-d party code.
Code must be run within try-catch blocks.

However, swallowing exception is not good idea. IMO it is better to crash
with a lot of noise then swallow exception and have inconsistent application
state.
--
With best regards, Vadym Stetsiak.
Blog: http://vadmyst.blogspot.com

You wrote on Thu, 1 Nov 2007 12:45:00 -0700:

 MS> Hi,

 MS> I'm trying to build a robust application framework that will be used
 MS> by other developers in the future. Their applications should be
 MS> able to recover themselves if they are crashing.

 MS> So, I thought, the best way to handle this is to lauch their stuff
 MS> in a separate AppDomain and when that AppDomain crashes, unload it
 MS> and restart it.
 MS> This seems to work fine, unless an unhandled exception occurs in a
 MS> background thread that is started from that new AppDomain. Instead
 MS> of just that AppDomain crashing, the complete application is
 MS> closed.

 MS> Does anyone know a way to prevent the application to close if an
 MS> unhandled exception occurs in a background thread that is started
 MS> from another
 MS> AppDomain?

 MS> Any help is welcome,

 MS> Marc

 MS> Below is a test application that demonstrates what goes wrong. The
 MS> goal is to keep the application running while changing nothing in
 MS> the Tester class, because that is the part that will be written by
 MS> other developers. I have no control over them if they do something
 MS> stupid like not handling any exceptions in background threads.

 MS> <code>
 MS> using System;

 MS> using System.Reflection;

 MS> using System.Threading;

 MS> using System.Windows.Forms;

 MS> namespace CrashRecoveryTest

 MS> {

 MS> internal class Program : MarshalByRefObject

 MS> {

 MS> private static void Main(string[] args)

 MS> {

 MS> try

 MS> {

 MS> Application.ThreadException += Application_ThreadException;

 MS> Application.SetUnhandledExceptionMode(UnhandledExceptionMode.
 MS> CatchException);

 MS> AppDomain.CurrentDomain.UnhandledException +=
 MS> CurrentDomain_UnhandledException;

 MS> Console.WriteLine("Starting new AppDomain");

 MS> for (int i = 0; i < 3; i++)

 MS> {

 MS> AppDomain newDomain = AppDomain.CreateDomain("TestDomain");

 MS> newDomain.UnhandledException += newDomain_UnhandledException;

 MS> Console.WriteLine("Loading Tester");

 MS> Tester tester =
 MS> (Tester)newDomain.CreateInstanceAndUnwrap(Assembly.
 MS> GetExecutingAssembly().GetName().Name, typeof(Tester).FullName);

 MS> Console.WriteLine("Calling Tester.Run method");

 MS> try

 MS> {

 MS> tester.Run();

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Caught exception " + ex.Message);

 MS> }

 MS> Console.WriteLine("Unloading AppDomain");

 MS> newDomain.UnhandledException -= newDomain_UnhandledException;

 MS> try

 MS> {

 MS> AppDomain.Unload(newDomain);

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Cannot unload AppDomain: " + ex.Message);

 MS> }

 MS> }

 MS> }

 MS> catch (Exception ex)

 MS> {

 MS> Console.WriteLine("Exception occurred: "+ex.Message );

 MS> }

 MS> Console.WriteLine("Application ended ok");

 MS> }

 MS> private static void Application_ThreadException(object sender,
 MS> ThreadExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Application.ThreadException occurred");

 MS> }

 MS> private static void CurrentDomain_UnhandledException(object sender,
 MS> UnhandledExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Unhandled exception occurred in default
 MS> AppDomain");

 MS> }

 MS> private static void newDomain_UnhandledException(object sender,
 MS> UnhandledExceptionEventArgs e)

 MS> {

 MS> Console.WriteLine("Exception occurred in TestDomain");

 MS> }

 MS> }

 MS> public class Tester : MarshalByRefObject

 MS> {

 MS> public Tester()

 MS> {

 MS> Console.WriteLine("Tester is started in AppDomain " +
 MS> AppDomain.CurrentDomain.FriendlyName);

 MS> }

 MS> public void Run()

 MS> {

 MS> Console.WriteLine("Starting worker thread...");

 MS> Thread workerThread = new Thread(DoWork);

 MS> workerThread.Start();

 MS> Console.WriteLine("Doing some interesting work...");

 MS> for (int i = 0; i < 10; i++)

 MS> Thread.Sleep(1000);

 MS> workerThread.Join();

 MS> }

 MS> private static void DoWork()

 MS> {

 MS> Console.WriteLine("Worker thread is running in AppDomain "+
 MS> AppDomain.CurrentDomain.FriendlyName);

 MS> Thread.Sleep(3000);

 MS> Console.WriteLine("Worker thread about to throw an exception...");

 MS> throw new ApplicationException("Oops");

 MS> }

 MS> }

 MS> }

 MS> </code>