[threads] Enable blocking transition with MONO_ENABLE_BLOCKING_TRANSITION env variabl...
authorLudovic Henry <ludovic@xamarin.com>
Thu, 22 Jun 2017 00:46:18 +0000 (20:46 -0400)
committerGitHub <noreply@github.com>
Thu, 22 Jun 2017 00:46:18 +0000 (20:46 -0400)
mono/metadata/marshal.c
mono/metadata/threads.c
mono/mini/interp/interp.c
mono/mini/interp/mintops.def
mono/mini/interp/transform.c
mono/mini/method-to-ir.c
mono/mini/mini-runtime.c
mono/utils/mono-threads-coop.c
mono/utils/mono-threads-coop.h
mono/utils/mono-threads.c

index 25fc058351e36735ff650c4dc7609040bcaaf118..f75d24736e4a6e0e817da04ee803f7de69e4ca31 100644 (file)
@@ -7692,7 +7692,7 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
                mono_mb_add_local (mb, sig->ret);
        }
 
-       if (mono_threads_is_coop_enabled ()) {
+       if (mono_threads_is_blocking_transition_enabled ()) {
                /* local 4, dummy local used to get a stack address for suspend funcs */
                coop_gc_stack_dummy = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
                /* local 5, the local to be used when calling the suspend funcs */
@@ -7733,7 +7733,7 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
        }
 
        // In coop mode need to register blocking state during native call
-       if (mono_threads_is_coop_enabled ()) {
+       if (mono_threads_is_blocking_transition_enabled ()) {
                // Perform an extra, early lookup of the function address, so any exceptions
                // potentially resulting from the lookup occur before entering blocking mode.
                if (!func_param && !MONO_CLASS_IS_IMPORT (mb->method->klass) && aot) {
@@ -7818,7 +7818,7 @@ mono_marshal_emit_native_wrapper (MonoImage *image, MonoMethodBuilder *mb, MonoM
        }
 
        /* Unblock before converting the result, since that can involve calls into the runtime */
-       if (mono_threads_is_coop_enabled ()) {
+       if (mono_threads_is_blocking_transition_enabled ()) {
                mono_mb_emit_ldloc (mb, coop_gc_var);
                mono_mb_emit_ldloc_addr (mb, coop_gc_stack_dummy);
                mono_mb_emit_icall (mb, mono_threads_exit_gc_safe_region_unbalanced);
@@ -8587,7 +8587,7 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
        /* try { */
        clause_catch->try_offset = clause_finally->try_offset = mono_mb_get_label (mb);
 
-       if (!mono_threads_is_coop_enabled ()) {
+       if (!mono_threads_is_blocking_transition_enabled ()) {
                mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
                mono_mb_emit_byte (mb, CEE_MONO_JIT_ATTACH);
        } else {
@@ -8769,7 +8769,7 @@ mono_marshal_emit_managed_wrapper (MonoMethodBuilder *mb, MonoMethodSignature *i
        clause_finally->try_len = mono_mb_get_label (mb) - clause_finally->try_offset;
        clause_finally->handler_offset = mono_mb_get_label (mb);
 
-       if (!mono_threads_is_coop_enabled ()) {
+       if (!mono_threads_is_blocking_transition_enabled ()) {
                mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
                mono_mb_emit_byte (mb, CEE_MONO_JIT_DETACH);
        } else {
index 69a00d0b6880b51fd2f4a92f6c52973468f77486..e19f7e0735193cadde3d8bff77de62b48516ad75 100644 (file)
@@ -5107,35 +5107,32 @@ ves_icall_System_Threading_Thread_GetStackTraces (MonoArray **out_threads, MonoA
 /*
  * mono_threads_attach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
- *  - @dummy: is NULL
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  *  - @return: the original domain which needs to be restored, or NULL.
- *
- * In coop mode:
- *  - @dummy: contains the original domain
- *  - @return: a cookie containing current MonoThreadInfo*.
  */
 gpointer
 mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
 {
        MonoDomain *orig;
-       gboolean fresh_thread = FALSE;
+       MonoThreadInfo *info;
+       gboolean external;
+
+       orig = mono_domain_get ();
 
        if (!domain) {
                /* Happens when called from AOTed code which is only used in the root domain. */
                domain = mono_get_root_domain ();
+               g_assert (domain);
        }
 
-       g_assert (domain);
-
        /* On coop, when we detached, we moved the thread from  RUNNING->BLOCKING.
         * If we try to reattach we do a BLOCKING->RUNNING transition.  If the thread
         * is fresh, mono_thread_attach() will do a STARTING->RUNNING transition so
         * we're only responsible for making the cookie. */
-       if (mono_threads_is_coop_enabled ()) {
-               MonoThreadInfo *info = mono_thread_info_current_unchecked ();
-               fresh_thread = !info || !mono_thread_info_is_live (info);
-       }
+       if (mono_threads_is_blocking_transition_enabled ())
+               external = !(info = mono_thread_info_current_unchecked ()) || !mono_thread_info_is_live (info);
 
        if (!mono_thread_internal_current ()) {
                mono_thread_attach_full (domain, FALSE);
@@ -5144,61 +5141,52 @@ mono_threads_attach_coop (MonoDomain *domain, gpointer *dummy)
                mono_thread_set_state (mono_thread_internal_current (), ThreadState_Background);
        }
 
-       orig = mono_domain_get ();
        if (orig != domain)
                mono_domain_set (domain, TRUE);
 
-       if (!mono_threads_is_coop_enabled ())
-               return orig != domain ? orig : NULL;
-
-       if (fresh_thread) {
-               *dummy = NULL;
-               /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
-                * return the right cookie. */
-               return mono_threads_enter_gc_unsafe_region_cookie ();
-       } else {
-               *dummy = orig;
-               /* thread state (BLOCKING|RUNNING) -> RUNNING */
-               return mono_threads_enter_gc_unsafe_region (dummy);
+       if (mono_threads_is_blocking_transition_enabled ()) {
+               if (external) {
+                       /* mono_thread_attach put the thread in RUNNING mode from STARTING, but we need to
+                        * return the right cookie. */
+                       *dummy = mono_threads_enter_gc_unsafe_region_cookie ();
+               } else {
+                       /* thread state (BLOCKING|RUNNING) -> RUNNING */
+                       *dummy = mono_threads_enter_gc_unsafe_region (dummy);
+               }
        }
+
+       return orig;
 }
 
 /*
  * mono_threads_detach_coop: called by native->managed wrappers
  *
- * In non-coop mode:
  *  - @cookie: the original domain which needs to be restored, or NULL.
- *  - @dummy: is NULL
- *
- * In coop mode:
- *  - @cookie: contains current MonoThreadInfo* if it was in BLOCKING mode, NULL otherwise
- *  - @dummy: contains the original domain
+ *  - @dummy:
+ *    - blocking mode: contains gc unsafe transition cookie
+ *    - non-blocking mode: contains random data
  */
 void
 mono_threads_detach_coop (gpointer cookie, gpointer *dummy)
 {
        MonoDomain *domain, *orig;
 
-       if (!mono_threads_is_coop_enabled ()) {
-               orig = (MonoDomain*) cookie;
-               if (orig)
-                       mono_domain_set (orig, TRUE);
-       } else {
-               orig = (MonoDomain*) *dummy;
+       orig = (MonoDomain*) cookie;
 
-               domain = mono_domain_get ();
-               g_assert (domain);
+       domain = mono_domain_get ();
+       g_assert (domain);
 
+       if (mono_threads_is_blocking_transition_enabled ()) {
                /* it won't do anything if cookie is NULL
                 * thread state RUNNING -> (RUNNING|BLOCKING) */
-               mono_threads_exit_gc_unsafe_region (cookie, dummy);
+               mono_threads_exit_gc_unsafe_region (*dummy, dummy);
+       }
 
-               if (orig != domain) {
-                       if (!orig)
-                               mono_domain_unset ();
-                       else
-                               mono_domain_set (orig, TRUE);
-               }
+       if (orig != domain) {
+               if (!orig)
+                       mono_domain_unset ();
+               else
+                       mono_domain_set (orig, TRUE);
        }
 }
 
index 14a0908289b5e36f166246fd7e30e9e8c07d0fe7..3a14d6923357789c25eb54c3c7e0d5eb08e93f3c 100644 (file)
@@ -4522,6 +4522,11 @@ array_constructed:
                        ++ip;
                        mono_jit_set_domain (context->original_domain);
                        MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_MONO_LDDOMAIN)
+                       sp->data.p = mono_domain_get ();
+                       ++sp;
+                       ++ip;
+                       MINT_IN_BREAK;
                MINT_IN_CASE(MINT_SDB_INTR_LOC)
                        if (G_UNLIKELY (ss_enabled)) {
                                static void (*ss_tramp) (void);
index 331ec1671cc16bcdf577f7bceed14ae35505f53d..c270a2b18e47796337c5ce59b4807b4de8c31294 100644 (file)
@@ -519,6 +519,7 @@ OPDEF(MINT_MONO_ATOMIC_STORE_I4, "mono_atomic.store.i4", 1, MintOpNoArgs)
 OPDEF(MINT_MONO_MEMORY_BARRIER, "mono_memory_barrier", 1, MintOpNoArgs)
 OPDEF(MINT_MONO_JIT_ATTACH, "mono_jit_attach", 1, MintOpNoArgs)
 OPDEF(MINT_MONO_JIT_DETACH, "mono_jit_detach", 1, MintOpNoArgs)
+OPDEF(MINT_MONO_LDDOMAIN, "mono_lddomain", 1, MintOpNoArgs)
 
 // FIXME: MintOp
 OPDEF(MINT_JIT_CALL, "mono_jit_call", 2, MintOpNoArgs)
index 93786da7f13a508b19b7513a9714ebb4a9bb6db4..35592cb05e2bafe26b4b476b9c0925cd962710d5 100644 (file)
@@ -3585,6 +3585,12 @@ generate (MonoMethod *method, RuntimeMethod *rtm, unsigned char *is_bb_start, Mo
                                ADD_CODE (&td, MINT_MONO_JIT_DETACH);
                                ++td.ip;
                                break;
+                       case CEE_MONO_LDDOMAIN:
+                               ADD_CODE (&td, MINT_MONO_LDDOMAIN);
+                               td.sp [0].type = STACK_TYPE_I;
+                               ++td.sp;
+                               ++td.ip;
+                               break;
                        default:
                                g_error ("transform.c: Unimplemented opcode: 0xF0 %02x at 0x%x\n", *td.ip, td.ip-header->code);
                        }
index c802e82394c49be55a0eea08c4524452dec134b2..619b1e68aa41d5d950a2c877a9f4df482b3c6a4f 100644 (file)
@@ -11765,7 +11765,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                MonoInst *ad_ins, *jit_tls_ins;
                                MonoBasicBlock *next_bb = NULL, *call_bb = NULL;
 
-                               g_assert (!mono_threads_is_coop_enabled ());
+                               g_assert (!mono_threads_is_blocking_transition_enabled ());
 
                                cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
 
index ec748153504cadb90ffcddd52c5fac6a2a4cdd16..b6ecda34d603b0f4e07a7d91e111bd30c4bdf19d 100644 (file)
@@ -840,7 +840,7 @@ mono_jit_thread_attach (MonoDomain *domain)
        MonoDomain *orig;
        gboolean attached;
 
-       g_assert (!mono_threads_is_coop_enabled ());
+       g_assert (!mono_threads_is_blocking_transition_enabled ());
 
        if (!domain) {
                /* Happens when called from AOTed code which is only used in the root domain. */
@@ -873,7 +873,7 @@ mono_jit_thread_attach (MonoDomain *domain)
 void
 mono_jit_set_domain (MonoDomain *domain)
 {
-       g_assert (!mono_threads_is_coop_enabled ());
+       g_assert (!mono_threads_is_blocking_transition_enabled ());
 
        if (domain)
                mono_domain_set (domain, TRUE);
index 0d5f1e9b73577e3ce129005e5dedf4de73dc3972..440eff85125121729d9c330de899bf5d74e43d0f 100644 (file)
@@ -117,7 +117,7 @@ mono_threads_state_poll (void)
 static void
 mono_threads_state_poll_with_info (MonoThreadInfo *info)
 {
-       g_assert (mono_threads_is_coop_enabled ());
+       g_assert (mono_threads_is_blocking_transition_enabled ());
 
        ++coop_do_polling_count;
 
@@ -206,7 +206,7 @@ mono_threads_enter_gc_safe_region_with_info (MonoThreadInfo *info, gpointer *sta
 {
        gpointer cookie;
 
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return NULL;
 
        cookie = mono_threads_enter_gc_safe_region_unbalanced_with_info (info, stackdata);
@@ -228,7 +228,7 @@ mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackdata)
 static gpointer
 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return NULL;
 
        ++coop_do_blocking_count;
@@ -255,7 +255,7 @@ retry:
 void
 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackdata)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return;
 
 #ifdef ENABLE_CHECKED_BUILD_GC
@@ -271,7 +271,7 @@ mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackdat
 {
        MonoThreadInfo *info;
 
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return;
 
        info = (MonoThreadInfo *)cookie;
@@ -314,7 +314,7 @@ mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, gpointer
 {
        gpointer cookie;
 
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return NULL;
 
        cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata);
@@ -336,7 +336,7 @@ mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackdata)
 gpointer
 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return NULL;
 
        ++coop_reset_blocking_count;
@@ -376,7 +376,7 @@ mono_threads_enter_gc_unsafe_region_cookie (void)
 {
        MonoThreadInfo *info;
 
-       g_assert (mono_threads_is_coop_enabled ());
+       g_assert (mono_threads_is_blocking_transition_enabled ());
 
        info = mono_thread_info_current_unchecked ();
 
@@ -393,7 +393,7 @@ mono_threads_enter_gc_unsafe_region_cookie (void)
 void
 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackdata)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return;
 
 #ifdef ENABLE_CHECKED_BUILD_GC
@@ -407,7 +407,7 @@ mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackdata)
 void
 mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackdata)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_blocking_transition_enabled ())
                return;
 
        if (!cookie)
@@ -435,11 +435,24 @@ mono_threads_is_coop_enabled (void)
 #endif
 }
 
+gboolean
+mono_threads_is_blocking_transition_enabled (void)
+{
+#if defined(USE_COOP_GC)
+       return TRUE;
+#else
+       static int is_blocking_transition_enabled = -1;
+       if (G_UNLIKELY (is_blocking_transition_enabled == -1))
+               is_blocking_transition_enabled = (g_hasenv ("MONO_ENABLE_COOP") || g_hasenv ("MONO_ENABLE_BLOCKING_TRANSITION")) ? 1 : 0;
+       return is_blocking_transition_enabled == 1;
+#endif
+}
+
 
 void
 mono_threads_coop_init (void)
 {
-       if (!mono_threads_is_coop_enabled ())
+       if (!mono_threads_is_coop_enabled () && !mono_threads_is_blocking_transition_enabled ())
                return;
 
        mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
index ac2726e989071b3bb604d68818f765acc1479917..9937c78414923cac14359e40f861f57e4ce28396 100644 (file)
@@ -28,6 +28,9 @@ extern volatile size_t mono_polling_required;
 gboolean
 mono_threads_is_coop_enabled (void);
 
+gboolean
+mono_threads_is_blocking_transition_enabled (void);
+
 /* Internal API */
 
 void
index c7b4ae76384c6dc7ae7560ba8074a22f22aa09fa..e26ca1760c6fb49214c735925a064146b8005ec7 100644 (file)
@@ -965,7 +965,7 @@ suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
                if (interrupt_kernel)
                        mono_threads_suspend_abort_syscall (info);
 
-               break;
+               return info;
        default:
                g_assert_not_reached ();
        }
@@ -1086,27 +1086,35 @@ STW to make sure no unsafe pending suspend is in progress.
 static void
 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
 {
-       if (mono_threads_is_coop_enabled ()) {
-               g_assert (info);
-               g_assert (mono_thread_info_is_current (info));
-               g_assert (mono_thread_info_is_live (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);
+       MONO_ENTER_GC_SAFE_WITH_INFO(info);
 
-               int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
-               g_assert (res != -1);
+       int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
+       g_assert (res != -1);
 
-               MONO_EXIT_GC_SAFE_WITH_INFO;
-       } else {
-               int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
-               g_assert (res != -1);
-       }
+       MONO_EXIT_GC_SAFE_WITH_INFO;
 }
 
 void
 mono_thread_info_suspend_lock (void)
 {
-       mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
+       MonoThreadInfo *info;
+       gint res;
+
+       info = mono_thread_info_current_unchecked ();
+       if (info && mono_thread_info_is_live (info)) {
+               mono_thread_info_suspend_lock_with_info (info);
+               return;
+       }
+
+       /* mono_thread_info_suspend_lock () can be called from boehm-gc.c on_gc_notification before the new thread's
+        * start_wrapper calls mono_thread_info_attach but after pthread_create calls the start wrapper. */
+
+       res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
+       g_assert (res != -1);
 }
 
 void