Merge pull request #3136 from BrzVlad/fix-stw-stopped
[mono.git] / mono / metadata / sgen-mono.c
index 862871c771206b1b1fb73ecf4ccee1ed57bbf895..4750b0b2dc3462bae62d80a61a5de05e53bf3d9c 100644 (file)
@@ -26,6 +26,7 @@
 #include "metadata/handle.h"
 #include "utils/mono-memory-model.h"
 #include "utils/mono-logger-internals.h"
+#include "utils/mono-threads-coop.h"
 #include "sgen/sgen-thread-pool.h"
 
 #ifdef HEAVY_STATISTICS
@@ -840,7 +841,7 @@ mono_gc_clear_domain (MonoDomain * domain)
        sgen_stop_world (0);
 
        if (sgen_concurrent_collection_in_progress ())
-               sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE);
+               sgen_perform_collection (0, GENERATION_OLD, "clear domain", TRUE, FALSE);
        SGEN_ASSERT (0, !sgen_concurrent_collection_in_progress (), "We just ordered a synchronous collection.  Why are we collecting concurrently?");
 
        major_collector.finish_sweeping ();
@@ -897,7 +898,7 @@ mono_gc_clear_domain (MonoDomain * domain)
        major_collector.iterate_objects (ITERATE_OBJECTS_SWEEP_PINNED, (IterateObjectCallbackFunc)clear_domain_free_major_pinned_object_callback, domain);
 
        if (domain == mono_get_root_domain ()) {
-               sgen_pin_stats_print_class_stats ();
+               sgen_pin_stats_report ();
                sgen_object_layout_dump (stdout);
        }
 
@@ -993,13 +994,22 @@ static gboolean use_managed_allocator = TRUE;
 
 #ifdef HAVE_KW_THREAD
 
-#define EMIT_TLS_ACCESS_NEXT_ADDR(mb)  do {    \
+#define EMIT_TLS_ACCESS_VAR(_mb, _var) /* nothing to do */
+
+#define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, _var) \
+       do { \
+               mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
+               mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
+               mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_IN_CRITICAL_REGION_ADDR); \
+       } while (0)
+
+#define EMIT_TLS_ACCESS_NEXT_ADDR(mb, _var)    do {    \
        mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
        mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
        mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_TLAB_NEXT_ADDR);            \
        } while (0)
 
-#define EMIT_TLS_ACCESS_TEMP_END(mb)   do {    \
+#define EMIT_TLS_ACCESS_TEMP_END(mb, _var)     do {    \
        mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
        mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
        mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_TLAB_TEMP_END);             \
@@ -1008,27 +1018,43 @@ static gboolean use_managed_allocator = TRUE;
 #else
 
 #if defined(TARGET_OSX) || defined(TARGET_WIN32) || defined(TARGET_ANDROID) || defined(TARGET_IOS)
-#define EMIT_TLS_ACCESS_NEXT_ADDR(mb)  do {    \
-       mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
-       mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
-       mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO);       \
+
+// Cache the SgenThreadInfo pointer in a local 'var'.
+#define EMIT_TLS_ACCESS_VAR(mb, var) \
+       do { \
+               var = mono_mb_add_local ((mb), &mono_defaults.int_class->byval_arg); \
+               mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX); \
+               mono_mb_emit_byte ((mb), CEE_MONO_TLS); \
+               mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO); \
+               mono_mb_emit_stloc ((mb), (var)); \
+       } while (0)
+
+#define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, var) \
+       do { \
+               mono_mb_emit_ldloc ((mb), (var)); \
+               mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenClientThreadInfo, in_critical_region)); \
+               mono_mb_emit_byte ((mb), CEE_ADD); \
+       } while (0)
+
+#define EMIT_TLS_ACCESS_NEXT_ADDR(mb, var)     do {    \
+       mono_mb_emit_ldloc ((mb), (var));               \
        mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_next_addr));  \
        mono_mb_emit_byte ((mb), CEE_ADD);              \
        mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
        } while (0)
 
-#define EMIT_TLS_ACCESS_TEMP_END(mb)   do {    \
-       mono_mb_emit_byte ((mb), MONO_CUSTOM_PREFIX);   \
-       mono_mb_emit_byte ((mb), CEE_MONO_TLS);         \
-       mono_mb_emit_i4 ((mb), TLS_KEY_SGEN_THREAD_INFO);       \
+#define EMIT_TLS_ACCESS_TEMP_END(mb, var)      do {    \
+       mono_mb_emit_ldloc ((mb), (var));               \
        mono_mb_emit_icon ((mb), MONO_STRUCT_OFFSET (SgenThreadInfo, tlab_temp_end));   \
        mono_mb_emit_byte ((mb), CEE_ADD);              \
        mono_mb_emit_byte ((mb), CEE_LDIND_I);          \
        } while (0)
 
 #else
-#define EMIT_TLS_ACCESS_NEXT_ADDR(mb)  do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
-#define EMIT_TLS_ACCESS_TEMP_END(mb)   do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
+#define EMIT_TLS_ACCESS_VAR(mb, _var)  do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
+#define EMIT_TLS_ACCESS_NEXT_ADDR(mb, _var)    do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
+#define EMIT_TLS_ACCESS_TEMP_END(mb, _var)     do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
+#define EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR(mb, _var)      do { g_error ("sgen is not supported when using --with-tls=pthread.\n"); } while (0)
 #endif
 
 #endif
@@ -1042,9 +1068,10 @@ static gboolean use_managed_allocator = TRUE;
  * that they are executed atomically via the restart mechanism.
  */
 static MonoMethod*
-create_allocator (int atype, gboolean slowpath)
+create_allocator (int atype, ManagedAllocatorVariant variant)
 {
-       int p_var, size_var;
+       int p_var, size_var, thread_var G_GNUC_UNUSED;
+       gboolean slowpath = variant == MANAGED_ALLOCATOR_SLOW_PATH;
        guint32 slowpath_branch, max_size_branch;
        MonoMethodBuilder *mb;
        MonoMethod *res;
@@ -1116,6 +1143,16 @@ create_allocator (int atype, gboolean slowpath)
                goto done;
        }
 
+       EMIT_TLS_ACCESS_VAR (mb, thread_var);
+
+#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
+       EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
+       mono_mb_emit_byte (mb, CEE_LDC_I4_1);
+       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+       mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
+       mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
+#endif
+
        size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
        if (atype == ATYPE_SMALL) {
                /* size_var = size_arg */
@@ -1265,7 +1302,7 @@ create_allocator (int atype, gboolean slowpath)
 
        /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
        tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-       EMIT_TLS_ACCESS_NEXT_ADDR (mb);
+       EMIT_TLS_ACCESS_NEXT_ADDR (mb, thread_var);
        mono_mb_emit_stloc (mb, tlab_next_addr_var);
 
        /* p = (void**)tlab_next; */
@@ -1284,7 +1321,7 @@ create_allocator (int atype, gboolean slowpath)
 
        /* if (G_LIKELY (new_next < tlab_temp_end)) */
        mono_mb_emit_ldloc (mb, new_next_var);
-       EMIT_TLS_ACCESS_TEMP_END (mb);
+       EMIT_TLS_ACCESS_TEMP_END (mb, thread_var);
        slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
 
        /* Slowpath */
@@ -1293,6 +1330,18 @@ create_allocator (int atype, gboolean slowpath)
 
        mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
        mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
+       /*
+        * We are no longer in a critical section. We need to do this before calling
+        * to unmanaged land in order to avoid stw deadlocks since unmanaged code
+        * might take locks.
+        */
+#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
+       EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
+       mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+       mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
+       mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_NONE);
+#endif
 
        /* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
        mono_mb_emit_ldarg (mb, 0);
@@ -1320,11 +1369,6 @@ create_allocator (int atype, gboolean slowpath)
        mono_mb_emit_ldloc (mb, new_next_var);
        mono_mb_emit_byte (mb, CEE_STIND_I);
 
-       /*The tlab store must be visible before the the vtable store. This could be replaced with a DDS but doing it with IL would be tricky. */
-       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
-       mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
-       mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
-
        /* *p = vtable; */
        mono_mb_emit_ldloc (mb, p_var);
        mono_mb_emit_ldarg (mb, 0);
@@ -1350,11 +1394,18 @@ create_allocator (int atype, gboolean slowpath)
                mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
        }
 
+#ifdef MANAGED_ALLOCATOR_CAN_USE_CRITICAL_REGION
+       EMIT_TLS_ACCESS_IN_CRITICAL_REGION_ADDR (mb, thread_var);
+       mono_mb_emit_byte (mb, CEE_LDC_I4_0);
+       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+       mono_mb_emit_byte (mb, CEE_MONO_ATOMIC_STORE_I4);
+#else
+       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+       mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
+#endif
        /*
        We must make sure both vtable and max_length are globaly visible before returning to managed land.
        */
-       mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
-       mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
        mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);
 
        /* return p */
@@ -1403,17 +1454,19 @@ mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box, gboolean know
                return NULL;
        if (known_instance_size && ALIGN_TO (klass->instance_size, SGEN_ALLOC_ALIGN) >= SGEN_MAX_SMALL_OBJ_SIZE)
                return NULL;
-       if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+       if (mono_class_has_finalizer (klass) || mono_class_is_marshalbyref (klass))
                return NULL;
        if (klass->rank)
                return NULL;
+       if (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS)
+               return NULL;
        if (klass->byval_arg.type == MONO_TYPE_STRING)
-               return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, FALSE);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_STRING, MANAGED_ALLOCATOR_REGULAR);
        /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
        if (known_instance_size)
-               return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, FALSE);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL, MANAGED_ALLOCATOR_REGULAR);
        else
-               return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, FALSE);
+               return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL, MANAGED_ALLOCATOR_REGULAR);
 #else
        return NULL;
 #endif
@@ -1433,7 +1486,7 @@ mono_gc_get_managed_array_allocator (MonoClass *klass)
                return NULL;
        g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
 
-       return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, FALSE);
+       return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR, MANAGED_ALLOCATOR_REGULAR);
 #else
        return NULL;
 #endif
@@ -1446,11 +1499,11 @@ sgen_set_use_managed_allocator (gboolean flag)
 }
 
 MonoMethod*
-mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
+mono_gc_get_managed_allocator_by_type (int atype, ManagedAllocatorVariant variant)
 {
 #ifdef MANAGED_ALLOCATION
        MonoMethod *res;
-       MonoMethod **cache = slowpath ? slowpath_alloc_method_cache : alloc_method_cache;
+       MonoMethod **cache;
 
        if (!use_managed_allocator)
                return NULL;
@@ -1458,11 +1511,17 @@ mono_gc_get_managed_allocator_by_type (int atype, gboolean slowpath)
        if (!mono_runtime_has_tls_get ())
                return NULL;
 
+       switch (variant) {
+       case MANAGED_ALLOCATOR_REGULAR: cache = alloc_method_cache; break;
+       case MANAGED_ALLOCATOR_SLOW_PATH: cache = slowpath_alloc_method_cache; break;
+       default: g_assert_not_reached (); break;
+       }
+
        res = cache [atype];
        if (res)
                return res;
 
-       res = create_allocator (atype, slowpath);
+       res = create_allocator (atype, variant);
        LOCK_GC;
        if (cache [atype]) {
                mono_free_method (res);
@@ -2191,6 +2250,8 @@ sgen_client_thread_register (SgenThreadInfo* info, void *stack_bottom_fallback)
        binary_protocol_thread_register ((gpointer)mono_thread_info_get_tid (info));
 
        SGEN_LOG (3, "registered thread %p (%p) stack end %p", info, (gpointer)mono_thread_info_get_tid (info), info->client_info.stack_end);
+
+       info->client_info.info.handle_stack = mono_handle_stack_alloc ();
 }
 
 void
@@ -2216,6 +2277,10 @@ sgen_client_thread_unregister (SgenThreadInfo *p)
 
        binary_protocol_thread_unregister ((gpointer)tid);
        SGEN_LOG (3, "unregister thread %p (%p)", p, (gpointer)tid);
+
+       HandleStack *handles = (HandleStack*) p->client_info.info.handle_stack;
+       p->client_info.info.handle_stack = NULL;
+       mono_handle_stack_free (handles);
 }
 
 void
@@ -2243,8 +2308,6 @@ thread_in_critical_region (SgenThreadInfo *info)
 static void
 sgen_thread_attach (SgenThreadInfo *info)
 {
-       mono_handle_arena_init ((MonoHandleArena**) &info->client_info.info.handle_arena);
-
        if (mono_gc_get_gc_callbacks ()->thread_attach_func && !info->client_info.runtime_data)
                info->client_info.runtime_data = mono_gc_get_gc_callbacks ()->thread_attach_func ();
 }
@@ -2260,8 +2323,6 @@ sgen_thread_detach (SgenThreadInfo *p)
         */
        if (mono_domain_get ())
                mono_thread_detach_internal (mono_thread_internal_current ());
-
-       mono_handle_arena_cleanup ((MonoHandleArena**) &p->client_info.info.handle_arena);
 }
 
 gboolean
@@ -2350,6 +2411,7 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
                                fprintf (stderr, "Precise stack mark not supported - disabling.\n");
                                conservative_stack_mark = TRUE;
                        }
+                       //FIXME we should eventually use the new stack_mark from coop
                        sgen_conservatively_pin_objects_from ((void **)aligned_stack_start, (void **)info->client_info.stack_end, start_nursery, end_nursery, PIN_TYPE_STACK);
                }
 
@@ -2360,6 +2422,7 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
                        {
                                // This is used on Coop GC for platforms where we cannot get the data for individual registers.
                                // We force a spill of all registers into the stack and pass a chunk of data into sgen.
+                               //FIXME under coop, for now, what we need to ensure is that we scan any extra memory from info->client_info.stack_end to stack_mark
                                MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX];
                                if (state && state->gc_stackdata) {
                                        sgen_conservatively_pin_objects_from ((void **)state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size),
@@ -2367,6 +2430,9 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
                                }
                        }
                }
+               if (precise && info->client_info.info.handle_stack) {
+                       mono_handle_stack_scan ((HandleStack*)info->client_info.info.handle_stack, (GcScanFunc)ctx.ops->copy_or_mark_object, ctx.queue);
+               }
        } FOREACH_THREAD_END
 }