[runtime] Fix an infinite loop in the runtime class init code. (#4455)
authorZoltan Varga <vargaz@gmail.com>
Thu, 2 Mar 2017 21:08:00 +0000 (16:08 -0500)
committerGitHub <noreply@github.com>
Thu, 2 Mar 2017 21:08:00 +0000 (16:08 -0500)
The following can happen:
* Thread T1 starts initialization of C1
* Thread T2 starts initialization of C2
* T2 starts initialization of C1, it gets blocked on T1.
* T1 finishes initializing C1.
* T1 starts initialization of C3.
* T1 starts initialization of C2. It sees that T2 is
  still blocked on T1, but its not actually blocked, since
  C1 is finished. Still, we record that T1 is blocked.
* T3 starts initialization of C2, it gets into an infinite
  loop in the while loop, since the blocked_thread_hash
  now indicates that T1 and T2 are blocked on each other.

The solution is to not mark a thread as blocked, if its waiting
for a thread which is waiting on a finished lock. Fixes hangs
when running csc.

mono/metadata/object.c

index 418cc818ca920170eb63a55b34fd3a9fc9843c15..bba8c17d17ec4d142458764a1d674876c54dc7ac 100644 (file)
@@ -440,6 +440,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                        return TRUE;
                }
                /* see if the thread doing the initialization is already blocked on this thread */
+               gboolean is_blocked = TRUE;
                blocked = GUINT_TO_POINTER (MONO_NATIVE_THREAD_ID_TO_UINT (lock->initializing_tid));
                while ((pending_lock = (TypeInitializationLock*) g_hash_table_lookup (blocked_thread_hash, blocked))) {
                        if (mono_native_thread_id_equals (pending_lock->initializing_tid, tid)) {
@@ -450,6 +451,7 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                                        /* the thread doing the initialization is blocked on this thread,
                                           but on a lock that has already been freed. It just hasn't got
                                           time to awake */
+                                       is_blocked = FALSE;
                                        break;
                                }
                        }
@@ -457,7 +459,8 @@ mono_runtime_class_init_full (MonoVTable *vtable, MonoError *error)
                }
                ++lock->waiting_count;
                /* record the fact that we are waiting on the initializing thread */
-               g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), lock);
+               if (is_blocked)
+                       g_hash_table_insert (blocked_thread_hash, GUINT_TO_POINTER (tid), lock);
        }
        mono_type_initialization_unlock ();