X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fsgen-stw.c;h=a8a1b9bf32c9f8437d6772e3031ddf03ea1e3625;hb=a7c9e39f0f58a1dcd41475f5623f301f14e31da7;hp=6af2a6f45c99e53e51f1967f775d56fb1a25961a;hpb=3c2fd4f371846cec6c8e8e780bbff66614185c26;p=mono.git diff --git a/mono/metadata/sgen-stw.c b/mono/metadata/sgen-stw.c index 6af2a6f45c9..a8a1b9bf32c 100644 --- a/mono/metadata/sgen-stw.c +++ b/mono/metadata/sgen-stw.c @@ -1,5 +1,6 @@ -/* - * sgen-stw.c: Stop the world functionality +/** + * \file + * Stop the world functionality * * Author: * Paolo Molaro (lupus@ximian.com) @@ -19,12 +20,13 @@ #include "sgen/sgen-gc.h" #include "sgen/sgen-protocol.h" #include "sgen/sgen-memory-governor.h" -#include "sgen/sgen-thread-pool.h" +#include "sgen/sgen-workers.h" #include "metadata/profiler-private.h" #include "sgen/sgen-client.h" #include "metadata/sgen-bridge-internals.h" #include "metadata/gc-internals.h" #include "utils/mono-threads.h" +#include "utils/mono-threads-debug.h" #define TV_DECLARE SGEN_TV_DECLARE #define TV_GETTIME SGEN_TV_GETTIME @@ -33,6 +35,18 @@ static void sgen_unified_suspend_restart_world (void); static void sgen_unified_suspend_stop_world (void); +static TV_DECLARE (end_of_last_stw); + +guint64 mono_time_since_last_stw () +{ + if (end_of_last_stw == 0) + return 0; + + TV_DECLARE (current_time); + TV_GETTIME (current_time); + return TV_ELAPSED (end_of_last_stw, current_time); +} + unsigned int sgen_global_stop_count = 0; inline static void* @@ -44,8 +58,6 @@ align_pointer (void *ptr) return (void*)p; } -static MonoContext cur_thread_ctx; - static void update_current_thread_stack (void *start) { @@ -54,10 +66,12 @@ update_current_thread_stack (void *start) info->client_info.stack_start = align_pointer (&stack_guard); g_assert (info->client_info.stack_start); - g_assert (info->client_info.stack_start >= info->client_info.stack_start_limit && info->client_info.stack_start < info->client_info.stack_end); + g_assert (info->client_info.stack_start >= info->client_info.info.stack_start_limit && info->client_info.stack_start < info->client_info.info.stack_end); #if !defined(MONO_CROSS_COMPILE) && MONO_ARCH_HAS_MONO_CONTEXT MONO_CONTEXT_GET_CURRENT (info->client_info.ctx); +#elif defined (HOST_WASM) + //nothing #else g_error ("Sgen STW requires a working mono-context"); #endif @@ -66,106 +80,6 @@ update_current_thread_stack (void *start) mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, NULL, &info->client_info.ctx); } -static gboolean -is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip) -{ - MonoJitInfo *ji; - - if (!mono_thread_internal_current ()) - /* Happens during thread attach */ - return FALSE; - - if (!ip || !domain) - return FALSE; - if (!sgen_has_critical_method ()) - return FALSE; - - /* - * mono_jit_info_table_find is not async safe since it calls into the AOT runtime to load information for - * missing methods (#13951). To work around this, we disable the AOT fallback. For this to work, the JIT needs - * to register the jit info for all GC critical methods after they are JITted/loaded. - */ - ji = mono_jit_info_table_find_internal (domain, (char *)ip, FALSE, FALSE); - if (!ji) - return FALSE; - - return sgen_is_critical_method (mono_jit_info_get_method (ji)); -} - -static int -restart_threads_until_none_in_managed_allocator (void) -{ - int num_threads_died = 0; - int sleep_duration = -1; - - for (;;) { - int restart_count = 0, restarted_count = 0; - /* restart all threads that stopped in the - allocator */ - FOREACH_THREAD (info) { - gboolean result; - if (info->client_info.skip || info->client_info.gc_disabled || info->client_info.suspend_done) - continue; - if (mono_thread_info_is_live (info) && - (!info->client_info.stack_start || info->client_info.in_critical_region || info->client_info.info.inside_critical_region || - is_ip_in_managed_allocator (info->client_info.stopped_domain, info->client_info.stopped_ip))) { - binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info)); - SGEN_LOG (3, "thread %p resumed.", (void*) (size_t) info->client_info.info.native_handle); - result = sgen_resume_thread (info); - if (result) { - ++restart_count; - } else { - info->client_info.skip = 1; - } - } else { - /* we set the stopped_ip to - NULL for threads which - we're not restarting so - that we can easily identify - the others */ - info->client_info.stopped_ip = NULL; - info->client_info.stopped_domain = NULL; - info->client_info.suspend_done = TRUE; - } - } FOREACH_THREAD_END - /* if no threads were restarted, we're done */ - if (restart_count == 0) - break; - - /* wait for the threads to signal their restart */ - sgen_wait_for_suspend_ack (restart_count); - - if (sleep_duration < 0) { - mono_thread_info_yield (); - sleep_duration = 0; - } else { - g_usleep (sleep_duration); - sleep_duration += 10; - } - - /* stop them again */ - FOREACH_THREAD (info) { - gboolean result; - if (info->client_info.skip || info->client_info.stopped_ip == NULL) - continue; - result = sgen_suspend_thread (info); - - if (result) { - ++restarted_count; - } else { - info->client_info.skip = 1; - } - } FOREACH_THREAD_END - /* some threads might have died */ - num_threads_died += restart_count - restarted_count; - /* wait for the threads to signal their suspension - again */ - sgen_wait_for_suspend_ack (restarted_count); - } - - return num_threads_died; -} - static void acquire_gc_locks (void) { @@ -192,13 +106,12 @@ sgen_client_stop_world (int generation) { TV_DECLARE (end_handshake); - /* notify the profiler of the leftovers */ - /* FIXME this is the wrong spot at we can STW for non collection reasons. */ - if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) - mono_sgen_gc_event_moves (); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD, generation)); acquire_gc_locks (); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_STOP_WORLD_LOCKED, generation)); + /* We start to scan after locks are taking, this ensures we won't be interrupted. */ sgen_process_togglerefs (); @@ -208,18 +121,12 @@ sgen_client_stop_world (int generation) SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer) (gsize) mono_native_thread_id_get ()); TV_GETTIME (stop_world_time); - if (mono_thread_info_unified_management_enabled ()) { - sgen_unified_suspend_stop_world (); - } else { - int count, dead; - 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); - } + sgen_unified_suspend_stop_world (); SGEN_LOG (3, "world stopped"); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_STOP_WORLD, generation)); + TV_GETTIME (end_handshake); time_stop_world += TV_ELAPSED (stop_world_time, end_handshake); @@ -230,18 +137,19 @@ sgen_client_stop_world (int generation) /* LOCKING: assumes the GC lock is held */ void -sgen_client_restart_world (int generation, GGTimingInfo *timing) +sgen_client_restart_world (int generation, gint64 *stw_time) { TV_DECLARE (end_sw); TV_DECLARE (start_handshake); - TV_DECLARE (end_bridge); - unsigned long usec, bridge_usec; + unsigned long usec; /* notify the profiler of the leftovers */ /* FIXME this is the wrong spot at we can STW for non collection reasons. */ - if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES)) + if (MONO_PROFILER_ENABLED (gc_moves)) mono_sgen_gc_event_moves (); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_PRE_START_WORLD, generation)); + FOREACH_THREAD (info) { info->client_info.stack_start = NULL; memset (&info->client_info.ctx, 0, sizeof (MonoContext)); @@ -249,18 +157,18 @@ sgen_client_restart_world (int generation, GGTimingInfo *timing) TV_GETTIME (start_handshake); - if (mono_thread_info_unified_management_enabled ()) - sgen_unified_suspend_restart_world (); - else - sgen_thread_handshake (FALSE); + sgen_unified_suspend_restart_world (); TV_GETTIME (end_sw); time_restart_world += TV_ELAPSED (start_handshake, end_sw); usec = TV_ELAPSED (stop_world_time, end_sw); max_pause_usec = MAX (usec, max_pause_usec); + end_of_last_stw = end_sw; SGEN_LOG (2, "restarted (pause time: %d usec, max: %d)", (int)usec, (int)max_pause_usec); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD, generation)); + /* * We must release the thread info suspend lock after doing * the thread handshake. Otherwise, if the GC stops the world @@ -273,13 +181,9 @@ sgen_client_restart_world (int generation, GGTimingInfo *timing) */ release_gc_locks (); - TV_GETTIME (end_bridge); - bridge_usec = TV_ELAPSED (end_sw, end_bridge); + MONO_PROFILER_RAISE (gc_event, (MONO_GC_EVENT_POST_START_WORLD_UNLOCKED, generation)); - if (timing) { - timing [0].stw_time = usec; - timing [0].bridge_time = bridge_usec; - } + *stw_time = usec; } void @@ -350,30 +254,35 @@ sgen_is_thread_in_current_stw (SgenThreadInfo *info, int *reason) static void sgen_unified_suspend_stop_world (void) { - int restart_counter; int sleep_duration = -1; mono_threads_begin_global_suspend (); - THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n"); + THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ())); FOREACH_THREAD (info) { - int reason; info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; - if (sgen_is_thread_in_current_stw (info, &reason)) { - info->client_info.skip = !mono_thread_info_begin_suspend (info); - THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip); - } else { - THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip, reason); + + int reason; + if (!sgen_is_thread_in_current_stw (info, &reason)) { + THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %s reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false", reason); + continue; } + + info->client_info.skip = !mono_thread_info_begin_suspend (info); + + THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_thread_info_current ()->client_info.suspend_done = TRUE; mono_threads_wait_pending_operations (); for (;;) { - restart_counter = 0; + gint restart_counter = 0; + FOREACH_THREAD (info) { + gint suspend_count; + int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); @@ -386,27 +295,28 @@ sgen_unified_suspend_stop_world (void) - 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_thread_info_in_critical_location (info)) { - gboolean res; - gint suspend_count = mono_thread_info_suspend_count (info); - if (!(suspend_count == 1)) - g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); - 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->client_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->client_info.in_critical_region); + if (!mono_thread_info_in_critical_location (info)) { info->client_info.suspend_done = TRUE; + + THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); + continue; } + + suspend_count = mono_thread_info_suspend_count (info); + if (!(suspend_count == 1)) + g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); + + info->client_info.skip = !mono_thread_info_begin_resume (info); + if (!info->client_info.skip) + restart_counter += 1; + + THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END + mono_threads_wait_pending_operations (); + if (restart_counter == 0) break; - mono_threads_wait_pending_operations (); if (sleep_duration < 0) { mono_thread_info_yield (); @@ -423,54 +333,53 @@ sgen_unified_suspend_stop_world (void) continue; } - if (mono_thread_info_is_running (info)) { - gboolean res = mono_thread_info_begin_suspend (info); - THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res); - if (!res) - info->client_info.skip = TRUE; + if (!mono_thread_info_is_running (info)) { + THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not running\n", mono_thread_info_get_tid (info)); + continue; } + + info->client_info.skip = !mono_thread_info_begin_suspend (info); + + THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_threads_wait_pending_operations (); } FOREACH_THREAD (info) { + gpointer stopped_ip; + int reason = 0; - if (sgen_is_thread_in_current_stw (info, &reason)) { - MonoThreadUnwindState *state; - - THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info)); - g_assert (info->client_info.suspend_done); - - state = mono_thread_info_get_suspend_state (info); - - info->client_info.ctx = state->ctx; - - if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN] || !state->unwind_data [MONO_UNWIND_DATA_LMF]) { - /* thread is starting or detaching, nothing to scan here */ - info->client_info.stopped_domain = NULL; - info->client_info.stopped_ip = NULL; - info->client_info.stack_start = NULL; - } else { - /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ - info->client_info.stopped_domain = (MonoDomain*) mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); - info->client_info.stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); - info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); - - /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */ - if (!info->client_info.stack_start - || info->client_info.stack_start < info->client_info.stack_start_limit - || info->client_info.stack_start >= info->client_info.stack_end) { - g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p", - info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end); - } - } + if (!sgen_is_thread_in_current_stw (info, &reason)) { + g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); - binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), info->client_info.stopped_ip); - } else { THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); - g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); + continue; + } + + g_assert (info->client_info.suspend_done); + + info->client_info.ctx = mono_thread_info_get_suspend_state (info)->ctx; + + /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ + info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); + + if (info->client_info.stack_start < info->client_info.info.stack_start_limit + || info->client_info.stack_start >= info->client_info.info.stack_end) { + /* + * Thread context is in unhandled state, most likely because it is + * dying. We don't scan it. + * FIXME We should probably rework and check the valid flag instead. + */ + info->client_info.stack_start = NULL; } + + stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); + + binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), stopped_ip); + + THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended, stopped_ip = %p, stack = %p -> %p\n", + mono_thread_info_get_tid (info), stopped_ip, info->client_info.stack_start, info->client_info.stack_start ? info->client_info.info.stack_end : NULL); } FOREACH_THREAD_END }