[runtime] Don't start aborting a thread if it's running any .cctor. Fixes #43320.
authorRodrigo Kumpera <kumpera@gmail.com>
Thu, 25 Aug 2016 23:39:50 +0000 (16:39 -0700)
committerRodrigo Kumpera <kumpera@gmail.com>
Thu, 1 Sep 2016 00:45:31 +0000 (17:45 -0700)
A thread abort must not start if the thread is running a .cctor.
OTOH, if the cctor self aborts, it should start.

Threads now track how many cctors they are running in the cctor_exec_depth variable.

The abort machinery (in async_abort_critical) checks that flag and bail from
starting the abort.

The cctor code (in mono_runtime_class_init_full) ensures that cctor_exec_depth is bumped
around execution of the cctor itself. Additionally, error handling was cleaned up as it was pretty
inconsistent.

mono/metadata/object-internals.h
mono/metadata/object.c
mono/metadata/threads.c

index 8c349ee9b8a83c62486c3594688efbfeeae6fa44..b3228abb4099f86212c10c8ea008a964a9c5a7a2 100644 (file)
@@ -387,7 +387,7 @@ struct _MonoInternalThread {
         * same field there.
         */
        gsize start_notify_refcount;
-       gpointer unused2;
+       gsize   cctor_exec_depth;
 };
 
 struct _MonoThread {
index 3f61428872f77097b9890ebc75896f26de35786d..90113482625ee3b3a2548f1badd63570178b8b15 100644 (file)
@@ -329,6 +329,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
        MonoNativeThreadId tid;
        int do_initialization = 0;
        MonoDomain *last_domain = NULL;
+       MonoException * pending_tae = NULL;
 
        mono_error_init (error);
 
@@ -431,14 +432,23 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
        if (do_initialization) {
                MonoException *exc = NULL;
+               MonoInternalThread *thread = mono_thread_internal_current ();
+               ++thread->cctor_exec_depth;
+               mono_memory_barrier ();
                mono_runtime_try_invoke (method, NULL, NULL, (MonoObject**) &exc, error);
-               if (exc != NULL && mono_error_ok (error)) {
-                       mono_error_set_exception_instance (error, exc);
-               }
+               --thread->cctor_exec_depth;
+
+               //exception extracted, error will be set to the right value later
+               if (exc == NULL && !mono_error_ok (error))//invoking failed but exc was not set
+                       exc = mono_error_convert_to_exception (error);
+               else
+                       mono_error_cleanup (error);
+
+               mono_error_init (error);
 
                /* If the initialization failed, mark the class as unusable. */
                /* Avoid infinite loops */
-               if (!(mono_error_ok(error) ||
+               if (!(!exc ||
                          (klass->image == mono_defaults.corlib &&
                           !strcmp (klass->name_space, "System") &&
                           !strcmp (klass->name, "TypeInitializationException")))) {
@@ -451,15 +461,9 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
 
                        MonoException *exc_to_throw = mono_get_exception_type_initialization_checked (full_name, exc, error);
                        g_free (full_name);
-                       return_val_if_nok (error, FALSE);
 
-                       mono_error_set_exception_instance (error, exc_to_throw);
+                       mono_error_assert_ok (error); //We can't recover from this, no way to fail a type we can't alloc a failure.
 
-                       MonoException *exc_to_store = mono_error_convert_to_exception (error);
-                       /* What we really want to do here is clone the error object and store one copy in the
-                        * domain's exception hash and use the other one to error out here. */
-                       mono_error_init (error);
-                       mono_error_set_exception_instance (error, exc_to_store);
                        /*
                         * Store the exception object so it could be thrown on subsequent
                         * accesses.
@@ -467,7 +471,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                        mono_domain_lock (domain);
                        if (!domain->type_init_exception_hash)
                                domain->type_init_exception_hash = mono_g_hash_table_new_type (mono_aligned_addr_hash, NULL, MONO_HASH_VALUE_GC, MONO_ROOT_SOURCE_DOMAIN, "type initialization exceptions table");
-                       mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_store);
+                       mono_g_hash_table_insert (domain->type_init_exception_hash, klass, exc_to_throw);
                        mono_domain_unlock (domain);
                }
 
@@ -475,6 +479,11 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                        mono_domain_set (last_domain, TRUE);
                lock->done = TRUE;
                mono_type_init_unlock (lock);
+               if (exc && mono_object_class (exc) == mono_defaults.threadabortexception_class)
+                       pending_tae = exc;
+               //TAEs are blocked around .cctors, they must escape as soon as no cctor is left to run.
+               if (!thread->cctor_exec_depth && !pending_tae && !mono_get_eh_callbacks ()->mono_current_thread_has_handle_block_guard ())
+                       pending_tae = mono_thread_resume_interruption ();
        } else {
                /* this just blocks until the initializing thread is done */
                mono_type_init_lock (lock);
@@ -495,7 +504,10 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                vtable->initialized = 1;
        mono_type_initialization_unlock ();
 
-       if (vtable->init_failed) {
+       //TAE wins over TIE
+       if (pending_tae)
+               mono_error_set_exception_instance (error, pending_tae);
+       else if (vtable->init_failed) {
                /* Either we were the initializing thread or we waited for the initialization */
                mono_error_set_exception_instance (error, get_type_init_exception_for_vtable (vtable));
                return FALSE;
index 43dd5a430bcc4082614c473c1cbe4cb2fe25338e..d840b6b16c932b1700b24b9c5bd021353d8774cd 100644 (file)
@@ -971,7 +971,7 @@ mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gb
        /* Check that the managed and unmanaged layout of MonoInternalThread matches */
 #ifndef MONO_CROSS_COMPILE
        if (mono_check_corlib_version () == NULL)
-               g_assert (((char*)&internal->unused2 - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
+               g_assert (((char*)&internal->cctor_exec_depth - (char*)internal) == mono_defaults.internal_thread_class->fields [mono_defaults.internal_thread_class->field.count - 1].offset);
 #endif
 
        return internal;
@@ -4794,6 +4794,13 @@ async_abort_critical (MonoThreadInfo *info, gpointer ud)
        if (mono_get_eh_callbacks ()->mono_install_handler_block_guard (mono_thread_info_get_suspend_state (info)))
                return MonoResumeThread;
 
+       /*
+       The target thread is running at least one .cctor, which must not be interrupted, so we give up.
+       The .cctor code will give them a chance when appropriate.
+       */
+       if (thread->cctor_exec_depth)
+               return MonoResumeThread;
+
        /*someone is already interrupting it*/
        if (InterlockedCompareExchange (&thread->interruption_requested, 1, 0) == 1)
                return MonoResumeThread;