Merge pull request #3389 from lambdageek/bug-43099
authorAleksey Kliger (λgeek) <akliger@gmail.com>
Thu, 11 Aug 2016 14:43:08 +0000 (10:43 -0400)
committerGitHub <noreply@github.com>
Thu, 11 Aug 2016 14:43:08 +0000 (10:43 -0400)
[coop] Temporarily restore MonoThreadInfo when TLS destructor runs.  Fixes #43099

mono/tests/pinvoke3.cs
mono/utils/mono-threads-state-machine.c
mono/utils/mono-threads.c

index 29ed8ea7eee8498f00e11aaf06d54b7109a8150d..8d5cfb619f0657d5ab751afc0f509ba163b296f9 100644 (file)
@@ -1105,6 +1105,49 @@ public class Tests {
                return res;
        }
 
+       class Worker {
+               volatile bool stop = false;
+               public void Stop () {
+                       stop = true;
+               }
+
+               public void Work () {
+                       while (!stop) {
+                               for (int i = 0; i < 100; i++) {
+                                       var a = new double[80000];
+                                       Thread.Sleep (0);
+                               }
+                               GC.Collect ();
+                       }
+               }
+       }
+
+       public static int test_43_thread_attach_detach_contested () {
+               // Test plan: we want to create a race between the GC
+               // and native threads detaching.  When a native thread
+               // calls a managed delegate, it's attached to the
+               // runtime by the wrapper.  It is detached when the
+               // thread is destroyed and the TLS key destructor for
+               // MonoThreadInfo runs.  That destructor wants to take
+               // the GC lock.  So we create a lot of native threads
+               // while at the same time running a worker that
+               // allocates garbage and invokes the collector.
+               var w = new Worker ();
+               Thread t = new Thread(new ThreadStart (w.Work));
+               t.Start ();
+
+               for (int count = 0; count < 500; count++) {
+                       int res = mono_test_marshal_thread_attach (delegate (int i) {
+                                       Thread.Sleep (0);
+                                       return i + 1;
+                               });
+               }
+               Thread.Sleep (1000);
+               w.Stop ();
+               t.Join ();
+               return 43; 
+
+       }
        /*
         * Appdomain save/restore
         */
index 43df55e5359359b7f7d30c7bcb685916451a4d66..9792d5dbe217f09528628012ed96ed83b8e6ee7b 100644 (file)
@@ -282,7 +282,7 @@ MonoSelfSupendResult
 mono_threads_transition_state_poll (MonoThreadInfo *info)
 {
        int raw_state, cur_state, suspend_count;
-       g_assert (info == mono_thread_info_current ());
+       g_assert (mono_thread_info_is_current (info));
 
 retry_state_change:
        UNWRAP_THREAD_STATE (raw_state, cur_state, suspend_count, info);
index 23a3e759815e66268a5b2504f2ba32e62f23ea6e..33796525b90bec8fca74b00d013382cf4eec6398 100644 (file)
@@ -402,6 +402,8 @@ unregister_thread (void *arg)
 
        info = (MonoThreadInfo *) arg;
        g_assert (info);
+       g_assert (mono_thread_info_is_current (info));
+       g_assert (mono_thread_info_is_live (info));
 
        small_id = info->small_id;
 
@@ -637,6 +639,20 @@ mono_thread_info_is_exiting (void)
        return FALSE;
 }
 
+#ifndef HOST_WIN32
+static void
+thread_info_key_dtor (void *arg)
+{
+       /* Put the MonoThreadInfo back for the duration of the
+        * unregister code.  In some circumstances the thread needs to
+        * take the GC lock which may block which requires a coop
+        * state transition. */
+       mono_native_tls_set_value (thread_info_key, arg);
+       unregister_thread (arg);
+       mono_native_tls_set_value (thread_info_key, NULL);
+}
+#endif
+
 void
 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
 {
@@ -648,7 +664,7 @@ mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
        res = mono_native_tls_alloc (&thread_info_key, NULL);
        res = mono_native_tls_alloc (&thread_exited_key, NULL);
 #else
-       res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread);
+       res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
        res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
 #endif
 
@@ -1026,6 +1042,8 @@ static void
 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
 {
        g_assert (info);
+       g_assert (mono_thread_info_is_current (info));
+       g_assert (mono_thread_info_is_live (info));
 
        MONO_ENTER_GC_SAFE_WITH_INFO(info);