Permalink
Browse files

Replicate .NET behavior for exceptions in the finalizer thread.

On .NET, when an exception occurs in the finalizer thread, it is
printed, the AppDomain.UnhandledException event is raised, and
the runtime is aborted (exact same thing as what happens when an
exception is thrown in a thread pool thread).

We now do the same.

Note that this is a breaking change; previously, we just silently
swallowed exceptions that occurred in the finalizer thread. Given
this, a test had to be adjusted (and renamed). An extra test has
been added to verify that the UnhandledException event is raised
correctly.
  • Loading branch information...
1 parent bfea12c commit 59883a039925be9ff3e478d265aa7be896fbda79 alexrp committed Apr 25, 2012
View
@@ -24,6 +24,7 @@
#include <mono/metadata/metadata-internals.h>
#include <mono/metadata/mono-mlist.h>
#include <mono/metadata/threadpool.h>
+#include <mono/metadata/threadpool-internals.h>
#include <mono/metadata/threads-types.h>
#include <mono/utils/mono-logger-internal.h>
#include <mono/metadata/gc-internal.h>
@@ -225,9 +226,8 @@ mono_gc_run_finalize (void *obj, void *data)
runtime_invoke (o, NULL, &exc, NULL);
- if (exc) {
- /* fixme: do something useful */
- }
+ if (exc)
+ mono_internal_thread_unhandled_exception (exc);
mono_domain_set_internal (caller_domain);
}
@@ -3,5 +3,6 @@
void mono_thread_pool_remove_socket (int sock) MONO_INTERNAL;
gboolean mono_thread_pool_is_queue_array (MonoArray *o) MONO_INTERNAL;
+void mono_internal_thread_unhandled_exception (MonoObject* exc) MONO_INTERNAL;
#endif
@@ -1440,20 +1440,8 @@ async_invoke_thread (gpointer data)
exc = mono_async_invoke (tp, ar);
if (tp_item_end_func)
tp_item_end_func (tp_item_user_data);
- if (exc && mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
- gboolean unloaded;
- MonoClass *klass;
-
- klass = exc->vtable->klass;
- unloaded = is_appdomainunloaded_exception (exc->vtable->domain, klass);
- if (!unloaded && klass != mono_defaults.threadabortexception_class) {
- mono_unhandled_exception (exc);
- if (mono_environment_exitcode_get () == 1)
- exit (255);
- }
- if (klass == mono_defaults.threadabortexception_class)
- mono_thread_internal_reset_abort (thread);
- }
+ if (exc)
+ mono_internal_thread_unhandled_exception (exc);
if (is_socket && tp->is_io) {
MonoSocketAsyncResult *state = (MonoSocketAsyncResult *) data;
@@ -1654,3 +1642,21 @@ mono_install_threadpool_item_hooks (MonoThreadPoolItemFunc begin_func, MonoThrea
tp_item_user_data = user_data;
}
+void
+mono_internal_thread_unhandled_exception (MonoObject* exc)
+{
+ if (mono_runtime_unhandled_exception_policy_get () == MONO_UNHANDLED_POLICY_CURRENT) {
+ gboolean unloaded;
+ MonoClass *klass;
+
+ klass = exc->vtable->klass;
+ unloaded = is_appdomainunloaded_exception (exc->vtable->domain, klass);
+ if (!unloaded && klass != mono_defaults.threadabortexception_class) {
+ mono_unhandled_exception (exc);
+ if (mono_environment_exitcode_get () == 1)
+ exit (255);
+ }
+ if (klass == mono_defaults.threadabortexception_class)
+ mono_thread_internal_reset_abort (mono_thread_internal_current ());
+ }
+}
@@ -238,6 +238,7 @@ BASE_TEST_CS_SRC= \
finalizer-abort.cs \
finalizer-exception.cs \
finalizer-exit.cs \
+ finalizer-thread.cs \
main-exit.cs \
main-returns-abort-resetabort.cs \
main-returns-background-abort-resetabort.cs \
@@ -1,39 +1,24 @@
-
-using System;
-using System.Collections;
+using System;
using System.Threading;
-public class foo {
- public static LocalDataStoreSlot dataslot = Thread.AllocateDataSlot();
- public static int final_count=0;
+public class FinalizerException {
+ ~FinalizerException () {
+ throw new Exception ();
+ }
- ~foo() {
- // Demonstrate that this is still the same thread
- string ID=(string)Thread.GetData(dataslot);
- if(ID==null) {
- Console.WriteLine("Set ID: foo");
- Thread.SetData(dataslot, "foo");
- }
+ public static int Main () {
+ AppDomain.CurrentDomain.UnhandledException += (sender, args) => {
+ Console.WriteLine ("caught");
+ Environment.Exit (0);
+ };
- // Don't run forever
- if(final_count++>10) {
- Environment.Exit(0);
- }
+ new FinalizerException ();
- Console.WriteLine("finalizer thread ID: {0}", (string)Thread.GetData(dataslot));
- throw new SystemException("wibble");
- }
+ GC.Collect ();
+ GC.WaitForPendingFinalizers ();
- public static int Main() {
- ArrayList list = new ArrayList ();
- Thread.SetData(dataslot, "ID is wibble");
- Environment.ExitCode = 2;
- while(true) {
- foo instance = new foo();
- list.Add (new WeakReference(instance));
- Thread.Sleep (0);
- }
- return 1;
- }
-}
+ Thread.Sleep (Timeout.Infinite); // infinite wait so we don't race against the unhandled exception callback
+ return 2;
+ }
+}
@@ -0,0 +1,41 @@
+
+using System;
+using System.Collections;
+using System.Threading;
+
+public class foo {
+ public static LocalDataStoreSlot dataslot = Thread.AllocateDataSlot();
+ public static int final_count=0;
+
+ ~foo() {
+ // Demonstrate that this is still the same thread
+ string ID=(string)Thread.GetData(dataslot);
+ if(ID==null) {
+ Console.WriteLine("Set ID: foo");
+ Thread.SetData(dataslot, "foo");
+ }
+
+ // Don't run forever
+ if(final_count++>10) {
+ Environment.Exit(0);
+ }
+
+ Console.WriteLine("finalizer thread ID: {0}", (string)Thread.GetData(dataslot));
+
+ if ((string)Thread.GetData(dataslot) != "foo")
+ throw new Exception ();
+ }
+
+ public static int Main() {
+ ArrayList list = new ArrayList ();
+ Thread.SetData(dataslot, "ID is wibble");
+ Environment.ExitCode = 2;
+ while(true) {
+ foo instance = new foo();
+ list.Add (new WeakReference(instance));
+ Thread.Sleep (0);
+ }
+ return 1;
+ }
+}
+

0 comments on commit 59883a0

Please sign in to comment.