Merge pull request #1659 from alexanderkyte/stringbuilder-referencesource
[mono.git] / mono / metadata / sgen-stw.c
old mode 100755 (executable)
new mode 100644 (file)
index 92e031d..b994e0b
 #include "utils/mono-time.h"
 #include "utils/dtrace.h"
 #include "utils/mono-counters.h"
+#include "utils/mono-threads.h"
 
 #define TV_DECLARE SGEN_TV_DECLARE
 #define TV_GETTIME SGEN_TV_GETTIME
 #define TV_ELAPSED SGEN_TV_ELAPSED
-#define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
+
+static int sgen_unified_suspend_restart_world (void);
+static int sgen_unified_suspend_stop_world (void);
 
 inline static void*
 align_pointer (void *ptr)
@@ -50,9 +53,9 @@ align_pointer (void *ptr)
 }
 
 #ifdef USE_MONO_CTX
-static MonoContext cur_thread_ctx = {0};
+static MonoContext cur_thread_ctx;
 #else
-static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
+static mword cur_thread_regs [ARCH_NUM_REGS];
 #endif
 
 static void
@@ -118,9 +121,9 @@ restart_threads_until_none_in_managed_allocator (void)
                   allocator */
                FOREACH_THREAD_SAFE (info) {
                        gboolean result;
-                       if (info->skip || info->gc_disabled)
+                       if (info->skip || info->gc_disabled || info->suspend_done)
                                continue;
-                       if (mono_thread_info_run_state (info) == STATE_RUNNING && (!info->stack_start || info->in_critical_region || info->info.inside_critical_region ||
+                       if (mono_thread_info_is_live (info) && (!info->stack_start || info->in_critical_region || info->info.inside_critical_region ||
                                        is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
                                binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
                                SGEN_LOG (3, "thread %p resumed.", (void*) (size_t) info->info.native_handle);
@@ -138,6 +141,7 @@ restart_threads_until_none_in_managed_allocator (void)
                                   the others */
                                info->stopped_ip = NULL;
                                info->stopped_domain = NULL;
+                               info->suspend_done = TRUE;
                        }
                } END_FOREACH_THREAD_SAFE
                /* if no threads were restarted, we're done */
@@ -202,8 +206,8 @@ count_cards (long long *major_total, long long *major_marked, long long *los_tot
 static TV_DECLARE (stop_world_time);
 static unsigned long max_pause_usec = 0;
 
-static long long time_stop_world;
-static long long time_restart_world;
+static guint64 time_stop_world;
+static guint64 time_restart_world;
 
 /* LOCKING: assumes the GC lock is held */
 int
@@ -225,18 +229,24 @@ sgen_stop_world (int generation)
        sgen_global_stop_count++;
        SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ());
        TV_GETTIME (stop_world_time);
-       count = sgen_thread_handshake (TRUE);
-       dead = restart_threads_until_none_in_managed_allocator ();
-       if (count < dead)
-               g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
-       count -= dead;
+
+       if (mono_thread_info_unified_management_enabled ()) {
+               count = sgen_unified_suspend_stop_world ();
+       } else {
+               count = sgen_thread_handshake (TRUE);
+               dead = restart_threads_until_none_in_managed_allocator ();
+               if (count < dead)
+                       g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
+               count -= dead;
+       }
 
        SGEN_LOG (3, "world stopped %d thread(s)", count);
        mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
        MONO_GC_WORLD_STOP_END ();
        if (binary_protocol_is_enabled ()) {
-               long long major_total, major_marked, los_total, los_marked;
-               count_cards (&major_total, &major_marked, &los_total, &los_marked);
+               long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
+               if (binary_protocol_is_heavy_enabled ())
+                       count_cards (&major_total, &major_marked, &los_total, &los_marked);
                binary_protocol_world_stopped (sgen_timestamp (), major_total, major_marked, los_total, los_marked);
        }
 
@@ -244,7 +254,8 @@ sgen_stop_world (int generation)
        time_stop_world += TV_ELAPSED (stop_world_time, end_handshake);
 
        sgen_memgov_collection_start (generation);
-       sgen_bridge_reset_data ();
+       if (sgen_need_bridge_processing ())
+               sgen_bridge_reset_data ();
 
        return count;
 }
@@ -261,8 +272,9 @@ sgen_restart_world (int generation, GGTimingInfo *timing)
        unsigned long usec, bridge_usec;
 
        if (binary_protocol_is_enabled ()) {
-               long long major_total, major_marked, los_total, los_marked;
-               count_cards (&major_total, &major_marked, &los_total, &los_marked);
+               long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
+               if (binary_protocol_is_heavy_enabled ())
+                       count_cards (&major_total, &major_marked, &los_total, &los_marked);
                binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
        }
 
@@ -282,7 +294,13 @@ sgen_restart_world (int generation, GGTimingInfo *timing)
        } END_FOREACH_THREAD
 
        TV_GETTIME (start_handshake);
-       count = sgen_thread_handshake (FALSE);
+
+       if (mono_thread_info_unified_management_enabled ())
+               count = sgen_unified_suspend_restart_world ();
+       else
+               count = sgen_thread_handshake (FALSE);
+
+
        TV_GETTIME (end_sw);
        time_restart_world += TV_ELAPSED (start_handshake, end_sw);
        usec = TV_ELAPSED (stop_world_time, end_sw);
@@ -306,7 +324,8 @@ sgen_restart_world (int generation, GGTimingInfo *timing)
 
        sgen_try_free_some_memory = TRUE;
 
-       sgen_bridge_processing_finish (generation);
+       if (sgen_need_bridge_processing ())
+               sgen_bridge_processing_finish (generation);
 
        TV_GETTIME (end_bridge);
        bridge_usec = TV_ELAPSED (end_sw, end_bridge);
@@ -324,8 +343,200 @@ sgen_restart_world (int generation, GGTimingInfo *timing)
 void
 sgen_init_stw (void)
 {
-       mono_counters_register ("World stop", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &time_stop_world);
-       mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &time_restart_world);
+       mono_counters_register ("World stop", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_stop_world);
+       mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_restart_world);
+}
+
+/* Unified suspend code */
+
+static gboolean
+sgen_is_thread_in_current_stw (SgenThreadInfo *info)
+{
+       /*
+       A thread explicitly asked to be skiped because it holds no managed state.
+       This is used by TP and finalizer threads.
+       FIXME Use an atomic variable for this to avoid everyone taking the GC LOCK.
+       */
+       if (info->gc_disabled) {
+               return FALSE;
+       }
+
+       /*
+       We have detected that this thread is failing/dying, ignore it.
+       FIXME: can't we merge this with thread_is_dying?
+       */
+       if (info->skip) {
+               return FALSE;
+       }
+
+       /*
+       Suspending the current thread will deadlock us, bad idea.
+       */
+       if (info == mono_thread_info_current ()) {
+               return FALSE;
+       }
+
+       /*
+       We can't suspend the workers that will do all the heavy lifting.
+       FIXME Use some state bit in SgenThreadInfo for this.
+       */
+       if (sgen_is_worker_thread (mono_thread_info_get_tid (info))) {
+               return FALSE;
+       }
+
+       /*
+       The thread has signaled that it started to detach, ignore it.
+       FIXME: can't we merge this with skip
+       */
+       if (!mono_thread_info_is_live (info)) {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static void
+update_sgen_info (SgenThreadInfo *info)
+{
+       char *stack_start;
+
+       /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */
+       info->stopped_domain = mono_thread_info_tls_get (info, TLS_KEY_DOMAIN);
+       info->stopped_ip = (gpointer) MONO_CONTEXT_GET_IP (&mono_thread_info_get_suspend_state (info)->ctx);
+       stack_start = (char*)MONO_CONTEXT_GET_SP (&mono_thread_info_get_suspend_state (info)->ctx) - REDZONE_SIZE;
+
+       /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */
+       if (stack_start < (char*)info->stack_start_limit || stack_start >= (char*)info->stack_end)
+               g_error ("BAD STACK");
+
+       info->stack_start = stack_start;
+#ifdef USE_MONO_CTX
+       info->ctx = mono_thread_info_get_suspend_state (info)->ctx;
+#else
+       g_assert_not_reached ();
+#endif
+}
+
+static int
+sgen_unified_suspend_stop_world (void)
+{
+       int restart_counter;
+       SgenThreadInfo *info;
+       int count = 0;
+       int sleep_duration = -1;
+
+       mono_threads_begin_global_suspend ();
+       THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n");
+
+       FOREACH_THREAD_SAFE (info) {
+               info->skip = FALSE;
+               info->suspend_done = FALSE;
+               if (sgen_is_thread_in_current_stw (info)) {
+                       info->skip = !mono_thread_info_begin_suspend (info, FALSE);
+                       THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), info->skip);
+                       if (!info->skip)
+                               ++count;
+               } else {
+                       THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d\n", mono_thread_info_get_tid (info), info->skip);
+               }
+       } END_FOREACH_THREAD_SAFE
+
+       mono_thread_info_current ()->suspend_done = TRUE;
+       mono_threads_wait_pending_operations ();
+
+       for (;;) {
+               restart_counter = 0;
+               FOREACH_THREAD_SAFE (info) {
+                       if (info->suspend_done || !sgen_is_thread_in_current_stw (info)) {
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE thread %p not been processed done %d current %d\n", mono_thread_info_get_tid (info), info->suspend_done, !sgen_is_thread_in_current_stw (info));
+                               continue;
+                       }
+
+                       /*
+                       All threads that reach here are pristine suspended. This means the following:
+
+                       - We haven't accepted the previous suspend as good.
+                       - We haven't gave up on it for this STW (it's either bad or asked not to)
+                       */
+                       if (!mono_threads_core_check_suspend_result (info)) {
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] SKIP thread %p failed to finish to suspend\n", mono_thread_info_get_tid (info));
+                               info->skip = TRUE;
+                       } else if (mono_thread_info_in_critical_location (info)) {
+                               gboolean res;
+                               g_assert (mono_thread_info_suspend_count (info) == 1);
+                               res = mono_thread_info_begin_resume (info);
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %d\n", mono_thread_info_get_tid (info), res);
+                               if (res)
+                                       ++restart_counter;
+                               else
+                                       info->skip = TRUE;
+                       } else {
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info));
+                               g_assert (!info->in_critical_region);
+                               info->suspend_done = TRUE;
+                       }
+               } END_FOREACH_THREAD_SAFE
+
+               if (restart_counter == 0)
+                       break;
+               mono_threads_wait_pending_operations ();
+
+               if (sleep_duration < 0) {
+#ifdef HOST_WIN32
+                       SwitchToThread ();
+#else
+                       sched_yield ();
+#endif
+                       sleep_duration = 0;
+               } else {
+                       g_usleep (sleep_duration);
+                       sleep_duration += 10;
+               }
+
+               FOREACH_THREAD_SAFE (info) {
+                       if (sgen_is_thread_in_current_stw (info) && mono_thread_info_is_running (info)) {
+                               gboolean res = mono_thread_info_begin_suspend (info, FALSE);
+                               THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res);
+                               if (!res)
+                                       info->skip = TRUE;
+                       }
+               } END_FOREACH_THREAD_SAFE
+
+               mono_threads_wait_pending_operations ();
+       }
+
+       FOREACH_THREAD_SAFE (info) {
+               if (sgen_is_thread_in_current_stw (info)) {
+                       THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info));
+                       g_assert (info->suspend_done);
+                       update_sgen_info (info);
+               } else {
+                       g_assert (!info->suspend_done || info == mono_thread_info_current ());
+               }
+       } END_FOREACH_THREAD_SAFE
+
+       return count;
 }
 
+static int
+sgen_unified_suspend_restart_world (void)
+{
+       SgenThreadInfo *info;
+       int count = 0;
+
+       THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n");
+       FOREACH_THREAD_SAFE (info) {
+               if (sgen_is_thread_in_current_stw (info)) {
+                       g_assert (mono_thread_info_begin_resume (info));
+                       THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info));
+                       ++count;
+               } else {
+                       THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p\n", mono_thread_info_get_tid (info));
+               }
+       } END_FOREACH_THREAD_SAFE
+
+       mono_threads_wait_pending_operations ();
+       mono_threads_end_global_suspend ();
+       return count;
+}
 #endif