From 9df453295e5f5f733aa1edf445f9acaf2354e8b7 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Wed, 21 Jun 2017 20:46:18 -0400 Subject: [PATCH] [threads] Enable blocking transition with MONO_ENABLE_BLOCKING_TRANSITION env variable (#5087) --- mono/metadata/marshal.c | 10 ++--- mono/metadata/threads.c | 82 +++++++++++++++------------------- mono/mini/interp/interp.c | 5 +++ mono/mini/interp/mintops.def | 1 + mono/mini/interp/transform.c | 6 +++ mono/mini/method-to-ir.c | 2 +- mono/mini/mini-runtime.c | 4 +- mono/utils/mono-threads-coop.c | 35 ++++++++++----- mono/utils/mono-threads-coop.h | 3 ++ mono/utils/mono-threads.c | 36 +++++++++------ 10 files changed, 104 insertions(+), 80 deletions(-) diff --git a/mono/metadata/marshal.c b/mono/metadata/marshal.c index 25fc058351e..f75d24736e4 100644 --- a/mono/metadata/marshal.c +++ b/mono/metadata/marshal.c @@ -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 { diff --git a/mono/metadata/threads.c b/mono/metadata/threads.c index 69a00d0b688..e19f7e07351 100644 --- a/mono/metadata/threads.c +++ b/mono/metadata/threads.c @@ -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); } } diff --git a/mono/mini/interp/interp.c b/mono/mini/interp/interp.c index 14a0908289b..3a14d692335 100644 --- a/mono/mini/interp/interp.c +++ b/mono/mini/interp/interp.c @@ -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); diff --git a/mono/mini/interp/mintops.def b/mono/mini/interp/mintops.def index 331ec1671cc..c270a2b18e4 100644 --- a/mono/mini/interp/mintops.def +++ b/mono/mini/interp/mintops.def @@ -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) diff --git a/mono/mini/interp/transform.c b/mono/mini/interp/transform.c index 93786da7f13..35592cb05e2 100644 --- a/mono/mini/interp/transform.c +++ b/mono/mini/interp/transform.c @@ -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); } diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index c802e82394c..619b1e68aa4 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -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); diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index ec748153504..b6ecda34d60 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -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); diff --git a/mono/utils/mono-threads-coop.c b/mono/utils/mono-threads-coop.c index 0d5f1e9b735..440eff85125 100644 --- a/mono/utils/mono-threads-coop.c +++ b/mono/utils/mono-threads-coop.c @@ -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); diff --git a/mono/utils/mono-threads-coop.h b/mono/utils/mono-threads-coop.h index ac2726e9890..9937c784149 100644 --- a/mono/utils/mono-threads-coop.h +++ b/mono/utils/mono-threads-coop.h @@ -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 diff --git a/mono/utils/mono-threads.c b/mono/utils/mono-threads.c index c7b4ae76384..e26ca1760c6 100644 --- a/mono/utils/mono-threads.c +++ b/mono/utils/mono-threads.c @@ -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 -- 2.25.1