#include "metadata/sgen-gc.h"
#include "metadata/sgen-protocol.h"
#include "metadata/sgen-memory-governor.h"
+#include "metadata/sgen-thread-pool.h"
#include "metadata/profiler-private.h"
#include "utils/mono-time.h"
-#include "utils/dtrace.h"
#include "utils/mono-counters.h"
+#include "utils/mono-threads.h"
+
+static gboolean world_is_stopped = FALSE;
#define TV_DECLARE SGEN_TV_DECLARE
#define TV_GETTIME SGEN_TV_GETTIME
#define TV_ELAPSED SGEN_TV_ELAPSED
+static int sgen_unified_suspend_restart_world (void);
+static int sgen_unified_suspend_stop_world (void);
+
inline static void*
align_pointer (void *ptr)
{
* 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, ip, FALSE);
+ ji = mono_jit_info_table_find_internal (domain, ip, FALSE, FALSE);
if (!ji)
return FALSE;
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);
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 */
{
TV_DECLARE (end_handshake);
int count, dead;
+ long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
+
+ SGEN_ASSERT (0, !world_is_stopped, "Why are we stopping a stopped world?");
mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
- MONO_GC_WORLD_STOP_BEGIN ();
binary_protocol_world_stopping (sgen_timestamp ());
acquire_gc_locks ();
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;
+ }
+
+ world_is_stopped = TRUE;
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 = -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);
- }
+ 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);
TV_GETTIME (end_handshake);
time_stop_world += TV_ELAPSED (stop_world_time, end_handshake);
TV_DECLARE (end_bridge);
unsigned long usec, bridge_usec;
+ SGEN_ASSERT (0, world_is_stopped, "Why are we restarting a running world?");
+
if (binary_protocol_is_enabled ()) {
long long major_total = -1, major_marked = -1, los_total = -1, los_marked = -1;
if (binary_protocol_is_heavy_enabled ())
if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES))
sgen_gc_event_moves ();
mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
- MONO_GC_WORLD_RESTART_BEGIN (generation);
+
FOREACH_THREAD (info) {
info->stack_start = NULL;
#ifdef USE_MONO_CTX
} 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);
max_pause_usec = MAX (usec, max_pause_usec);
+
+ world_is_stopped = FALSE;
+
SGEN_LOG (2, "restarted %d thread(s) (pause time: %d usec, max: %d)", count, (int)usec, (int)max_pause_usec);
mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
- MONO_GC_WORLD_RESTART_END (generation);
binary_protocol_world_restarted (generation, sgen_timestamp ());
/*
return count;
}
+gboolean
+sgen_is_world_stopped (void)
+{
+ return world_is_stopped;
+}
+
void
sgen_init_stw (void)
{
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_thread_pool_is_thread_pool_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