From c5512c0d3e2b102ed16158ce8c599c8203ccd510 Mon Sep 17 00:00:00 2001 From: Zoltan Varga Date: Wed, 10 Feb 2016 21:27:23 -0500 Subject: [PATCH] [jit] Add a fastpath to Monitor.Enter () which doesn't have a wrapper, and add a slowpath which has a wrapper, so async stack traces work for threads blocked on monitors. Fixes #38525. --- mono/metadata/monitor.c | 24 +++++++++++++++++++++++- mono/metadata/monitor.h | 3 +++ mono/mini/method-to-ir.c | 34 ++++++++++++++++++++++++++++++++-- mono/mini/mini-runtime.c | 19 +++++++++++++++++++ 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/mono/metadata/monitor.c b/mono/metadata/monitor.c index 0c1d28130af..b05a8416aac 100644 --- a/mono/metadata/monitor.c +++ b/mono/metadata/monitor.c @@ -1009,6 +1009,12 @@ mono_monitor_enter (MonoObject *obj) } gboolean +mono_monitor_enter_fast (MonoObject *obj) +{ + return mono_monitor_try_enter_internal (obj, 0, FALSE) == 1; +} + +gboolean mono_monitor_try_enter (MonoObject *obj, guint32 ms) { return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1; @@ -1100,7 +1106,6 @@ ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject void mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken) { - if (*lock_taken == 1) { mono_set_pending_exception (mono_get_exception_argument ("lockTaken", "lockTaken is already true")); return; @@ -1109,6 +1114,23 @@ mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken) ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (obj, INFINITE, lock_taken); } +/* + * mono_monitor_enter_v4_fast: + * + * Same as mono_monitor_enter_v4, but return immediately if the + * monitor cannot be acquired. + * Returns TRUE if the lock was acquired, FALSE otherwise. + */ +gboolean +mono_monitor_enter_v4_fast (MonoObject *obj, char *lock_taken) +{ + if (*lock_taken == 1) + return FALSE; + gint32 res = mono_monitor_try_enter_internal (obj, 0, TRUE); + *lock_taken = res == 1; + return res == 1; +} + gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj) { diff --git a/mono/metadata/monitor.h b/mono/metadata/monitor.h index bd796254034..f8e89936ba9 100644 --- a/mono/metadata/monitor.h +++ b/mono/metadata/monitor.h @@ -106,6 +106,9 @@ MONO_API void mono_locks_dump (gboolean include_untaken); void mono_monitor_init (void); void mono_monitor_cleanup (void); +gboolean mono_monitor_enter_fast (MonoObject *obj); +gboolean mono_monitor_enter_v4_fast (MonoObject *obj, char *lock_taken); + guint32 mono_monitor_get_object_monitor_gchandle (MonoObject *object); void mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset); diff --git a/mono/mini/method-to-ir.c b/mono/mini/method-to-ir.c index edece583b1c..d804fa67259 100644 --- a/mono/mini/method-to-ir.c +++ b/mono/mini/method-to-ir.c @@ -56,6 +56,7 @@ #include #include #include +#include #include #include #include @@ -5981,7 +5982,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else return NULL; } else if (cmethod->klass == mono_defaults.object_class) { - if (strcmp (cmethod->name, "GetType") == 0 && fsig->param_count + fsig->hasthis == 1) { int dreg = alloc_ireg_ref (cfg); int vt_reg = alloc_preg (cfg); @@ -6079,12 +6079,42 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign } else return NULL; } else if (cmethod->klass == runtime_helpers_class) { - if (strcmp (cmethod->name, "get_OffsetToStringData") == 0 && fsig->param_count == 0) { EMIT_NEW_ICONST (cfg, ins, MONO_STRUCT_OFFSET (MonoString, chars)); return ins; } else return NULL; + } else if (cmethod->klass == mono_defaults.monitor_class) { + gboolean is_enter = FALSE; + gboolean is_v4 = FALSE; + + if (!strcmp (cmethod->name, "enter_with_atomic_var") && mono_method_signature (cmethod)->param_count == 2) { + is_enter = TRUE; + is_v4 = TRUE; + } + if (!strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) + is_enter = TRUE; + + if (is_enter) { + /* + * To make async stack traces work, icalls which can block should have a wrapper. + * For Monitor.Enter, emit two calls: a fastpath which doesn't have a wrapper, and a slowpath, which does. + */ + MonoBasicBlock *end_bb, *slowpath_bb; + + NEW_BBLOCK (cfg, end_bb); + NEW_BBLOCK (cfg, slowpath_bb); + + ins = mono_emit_jit_icall (cfg, is_v4 ? mono_monitor_enter_v4_fast : mono_monitor_enter_fast, args); + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ins->dreg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_IBEQ, slowpath_bb); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); + MONO_START_BB (cfg, slowpath_bb); + ins = mono_emit_jit_icall (cfg, is_v4 ? mono_monitor_enter_v4 : mono_monitor_enter, args); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); + MONO_START_BB (cfg, end_bb); + return ins; + } } else if (cmethod->klass == mono_defaults.thread_class) { if (strcmp (cmethod->name, "SpinWait_nop") == 0 && fsig->param_count == 0) { MONO_INST_NEW (cfg, ins, OP_RELAXED_NOP); diff --git a/mono/mini/mini-runtime.c b/mono/mini/mini-runtime.c index 997f198c80b..52aaacbdd55 100644 --- a/mono/mini/mini-runtime.c +++ b/mono/mini/mini-runtime.c @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -711,6 +712,19 @@ register_icall_no_wrapper (gpointer func, const char *name, const char *sigstr) mono_register_jit_icall_full (func, name, sig, TRUE, FALSE, name); } +static void +register_icall_with_wrapper (gpointer func, const char *name, const char *sigstr) +{ + MonoMethodSignature *sig; + + if (sigstr) + sig = mono_create_icall_signature (sigstr); + else + sig = NULL; + + mono_register_jit_icall_full (func, name, sig, FALSE, FALSE, NULL); +} + static void register_dyn_icall (gpointer func, const char *name, const char *sigstr, gboolean save) { @@ -3948,6 +3962,11 @@ register_icalls (void) register_icall (mono_get_assembly_object, "mono_get_assembly_object", "object ptr", TRUE); register_icall (mono_get_method_object, "mono_get_method_object", "object ptr", TRUE); + register_icall_with_wrapper (mono_monitor_enter, "mono_monitor_enter", "void obj"); + register_icall_with_wrapper (mono_monitor_enter_v4, "mono_monitor_enter_v4", "void obj ptr"); + register_icall_no_wrapper (mono_monitor_enter_fast, "mono_monitor_enter_fast", "int obj"); + register_icall_no_wrapper (mono_monitor_enter_v4_fast, "mono_monitor_enter_v4_fast", "int obj ptr"); + #ifdef TARGET_IOS register_icall (pthread_getspecific, "pthread_getspecific", "ptr ptr", TRUE); #endif -- 2.25.1