From 57fb9aa164a835543f6b6b7707c778fd4de2a9b1 Mon Sep 17 00:00:00 2001 From: Aleksey Kliger Date: Wed, 10 Aug 2016 12:14:52 -0400 Subject: [PATCH] [coop] Temporarily restore MonoThreadInfo when TLS destructor runs. Fixes #43099 We need a valid MonoThreadInfo in the TLS key while we run unregister_thread because we may need to block the thread when it tries to take the sgen GC mutex. Fixes [#43099](https://bugzilla.xamarin.com/show_bug.cgi?id=43099) --- mono/tests/pinvoke3.cs | 43 +++++++++++++++++++++++++ mono/utils/mono-threads-state-machine.c | 2 +- mono/utils/mono-threads.c | 20 +++++++++++- 3 files changed, 63 insertions(+), 2 deletions(-) diff --git a/mono/tests/pinvoke3.cs b/mono/tests/pinvoke3.cs index 29ed8ea7eee..8d5cfb619f0 100644 --- a/mono/tests/pinvoke3.cs +++ b/mono/tests/pinvoke3.cs @@ -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 */ diff --git a/mono/utils/mono-threads-state-machine.c b/mono/utils/mono-threads-state-machine.c index 43df55e5359..9792d5dbe21 100644 --- a/mono/utils/mono-threads-state-machine.c +++ b/mono/utils/mono-threads-state-machine.c @@ -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); diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index 23a3e759815..33796525b90 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -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); -- 2.25.1