2 * sgen-stw.c: Stop the world functionality
5 * Paolo Molaro (lupus@ximian.com)
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
10 * Copyright 2011 Xamarin, Inc.
11 * Copyright (C) 2012 Xamarin Inc
13 * This library is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU Library General Public
15 * License 2.0 as published by the Free Software Foundation;
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Library General Public License for more details.
22 * You should have received a copy of the GNU Library General Public
23 * License 2.0 along with this library; if not, write to the Free
24 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include "metadata/sgen-gc.h"
31 #include "metadata/sgen-protocol.h"
32 #include "metadata/sgen-memory-governor.h"
33 #include "metadata/profiler-private.h"
34 #include "utils/mono-time.h"
35 #include "utils/dtrace.h"
37 #define TV_DECLARE SGEN_TV_DECLARE
38 #define TV_GETTIME SGEN_TV_GETTIME
39 #define TV_ELAPSED SGEN_TV_ELAPSED
40 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
43 align_pointer (void *ptr)
46 p += sizeof (gpointer) - 1;
47 p &= ~ (sizeof (gpointer) - 1);
52 static MonoContext cur_thread_ctx = {0};
54 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
58 update_current_thread_stack (void *start)
61 #if !defined(USE_MONO_CTX)
62 void *reg_ptr = cur_thread_regs;
64 SgenThreadInfo *info = mono_thread_info_current ();
66 info->stack_start = align_pointer (&stack_guard);
67 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
69 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
70 memcpy (&info->ctx, &cur_thread_ctx, sizeof (MonoContext));
71 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
72 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, NULL, &info->ctx);
74 ARCH_STORE_REGS (reg_ptr);
75 memcpy (&info->regs, reg_ptr, sizeof (info->regs));
76 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
77 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, NULL, NULL);
82 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
86 if (!mono_thread_internal_current ())
87 /* Happens during thread attach */
92 if (!sgen_has_critical_method ())
96 * mono_jit_info_table_find is not async safe since it calls into the AOT runtime to load information for
97 * missing methods (#13951). To work around this, we disable the AOT fallback. For this to work, the JIT needs
98 * to register the jit info for all GC critical methods after they are JITted/loaded.
100 ji = mono_jit_info_table_find_internal (domain, ip, FALSE);
104 return sgen_is_critical_method (mono_jit_info_get_method (ji));
108 restart_threads_until_none_in_managed_allocator (void)
110 SgenThreadInfo *info;
111 int num_threads_died = 0;
112 int sleep_duration = -1;
115 int restart_count = 0, restarted_count = 0;
116 /* restart all threads that stopped in the
118 FOREACH_THREAD_SAFE (info) {
120 if (info->skip || info->gc_disabled)
122 if (mono_thread_info_run_state (info) == STATE_RUNNING && (!info->stack_start || info->in_critical_region || info->info.inside_critical_region ||
123 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
124 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
125 SGEN_LOG (3, "thread %p resumed.", (void*) (size_t) info->info.native_handle);
126 result = sgen_resume_thread (info);
133 /* we set the stopped_ip to
134 NULL for threads which
135 we're not restarting so
136 that we can easily identify
138 info->stopped_ip = NULL;
139 info->stopped_domain = NULL;
141 } END_FOREACH_THREAD_SAFE
142 /* if no threads were restarted, we're done */
143 if (restart_count == 0)
146 /* wait for the threads to signal their restart */
147 sgen_wait_for_suspend_ack (restart_count);
149 if (sleep_duration < 0) {
150 mono_thread_info_yield ();
153 g_usleep (sleep_duration);
154 sleep_duration += 10;
157 /* stop them again */
158 FOREACH_THREAD (info) {
160 if (info->skip || info->stopped_ip == NULL)
162 result = sgen_suspend_thread (info);
170 /* some threads might have died */
171 num_threads_died += restart_count - restarted_count;
172 /* wait for the threads to signal their suspension
174 sgen_wait_for_suspend_ack (restarted_count);
177 return num_threads_died;
181 acquire_gc_locks (void)
184 mono_thread_info_suspend_lock ();
188 release_gc_locks (void)
190 mono_thread_info_suspend_unlock ();
195 count_cards (long long *major_total, long long *major_marked, long long *los_total, long long *los_marked)
197 sgen_get_major_collector ()->count_cards (major_total, major_marked);
198 sgen_los_count_cards (los_total, los_marked);
201 static TV_DECLARE (stop_world_time);
202 static unsigned long max_pause_usec = 0;
204 /* LOCKING: assumes the GC lock is held */
206 sgen_stop_world (int generation)
210 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
211 MONO_GC_WORLD_STOP_BEGIN ();
212 binary_protocol_world_stopping (sgen_timestamp ());
215 /* We start to scan after locks are taking, this ensures we won't be interrupted. */
216 sgen_process_togglerefs ();
218 update_current_thread_stack (&count);
220 sgen_global_stop_count++;
221 SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ());
222 TV_GETTIME (stop_world_time);
223 count = sgen_thread_handshake (TRUE);
224 dead = restart_threads_until_none_in_managed_allocator ();
226 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
229 SGEN_LOG (3, "world stopped %d thread(s)", count);
230 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
231 MONO_GC_WORLD_STOP_END ();
232 if (binary_protocol_is_enabled ()) {
233 long long major_total, major_marked, los_total, los_marked;
234 count_cards (&major_total, &major_marked, &los_total, &los_marked);
235 binary_protocol_world_stopped (sgen_timestamp (), major_total, major_marked, los_total, los_marked);
238 sgen_memgov_collection_start (generation);
239 sgen_bridge_reset_data ();
244 /* LOCKING: assumes the GC lock is held */
246 sgen_restart_world (int generation, GGTimingInfo *timing)
249 SgenThreadInfo *info;
251 TV_DECLARE (end_bridge);
252 unsigned long usec, bridge_usec;
254 if (binary_protocol_is_enabled ()) {
255 long long major_total, major_marked, los_total, los_marked;
256 count_cards (&major_total, &major_marked, &los_total, &los_marked);
257 binary_protocol_world_restarting (generation, sgen_timestamp (), major_total, major_marked, los_total, los_marked);
260 /* notify the profiler of the leftovers */
261 /* FIXME this is the wrong spot at we can STW for non collection reasons. */
262 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES))
263 sgen_gc_event_moves ();
264 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
265 MONO_GC_WORLD_RESTART_BEGIN (generation);
266 FOREACH_THREAD (info) {
267 info->stack_start = NULL;
269 memset (&info->ctx, 0, sizeof (MonoContext));
271 memset (&info->regs, 0, sizeof (info->regs));
275 count = sgen_thread_handshake (FALSE);
277 usec = TV_ELAPSED (stop_world_time, end_sw);
278 max_pause_usec = MAX (usec, max_pause_usec);
279 SGEN_LOG (2, "restarted %d thread(s) (pause time: %d usec, max: %d)", count, (int)usec, (int)max_pause_usec);
280 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
281 MONO_GC_WORLD_RESTART_END (generation);
282 binary_protocol_world_restarted (generation, sgen_timestamp ());
285 * We must release the thread info suspend lock after doing
286 * the thread handshake. Otherwise, if the GC stops the world
287 * and a thread is in the process of starting up, but has not
288 * yet registered (it's not in the thread_list), it is
289 * possible that the thread does register while the world is
290 * stopped. When restarting the GC will then try to restart
291 * said thread, but since it never got the suspend signal, it
292 * cannot answer the restart signal, so a deadlock results.
296 sgen_try_free_some_memory = TRUE;
298 sgen_bridge_processing_finish (generation);
300 TV_GETTIME (end_bridge);
301 bridge_usec = TV_ELAPSED (end_sw, end_bridge);
304 timing [0].stw_time = usec;
305 timing [0].bridge_time = bridge_usec;
308 sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);