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"
36 #include "utils/mono-counters.h"
38 #define TV_DECLARE SGEN_TV_DECLARE
39 #define TV_GETTIME SGEN_TV_GETTIME
40 #define TV_ELAPSED SGEN_TV_ELAPSED
41 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
44 align_pointer (void *ptr
)
47 p
+= sizeof (gpointer
) - 1;
48 p
&= ~ (sizeof (gpointer
) - 1);
53 static MonoContext cur_thread_ctx
;
55 static mword cur_thread_regs
[ARCH_NUM_REGS
];
59 update_current_thread_stack (void *start
)
62 #if !defined(USE_MONO_CTX)
63 void *reg_ptr
= cur_thread_regs
;
65 SgenThreadInfo
*info
= mono_thread_info_current ();
67 info
->stack_start
= align_pointer (&stack_guard
);
68 g_assert (info
->stack_start
>= info
->stack_start_limit
&& info
->stack_start
< info
->stack_end
);
70 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx
);
71 memcpy (&info
->ctx
, &cur_thread_ctx
, sizeof (MonoContext
));
72 if (mono_gc_get_gc_callbacks ()->thread_suspend_func
)
73 mono_gc_get_gc_callbacks ()->thread_suspend_func (info
->runtime_data
, NULL
, &info
->ctx
);
75 ARCH_STORE_REGS (reg_ptr
);
76 memcpy (&info
->regs
, reg_ptr
, sizeof (info
->regs
));
77 if (mono_gc_get_gc_callbacks ()->thread_suspend_func
)
78 mono_gc_get_gc_callbacks ()->thread_suspend_func (info
->runtime_data
, NULL
, NULL
);
83 is_ip_in_managed_allocator (MonoDomain
*domain
, gpointer ip
)
87 if (!mono_thread_internal_current ())
88 /* Happens during thread attach */
93 if (!sgen_has_critical_method ())
97 * mono_jit_info_table_find is not async safe since it calls into the AOT runtime to load information for
98 * missing methods (#13951). To work around this, we disable the AOT fallback. For this to work, the JIT needs
99 * to register the jit info for all GC critical methods after they are JITted/loaded.
101 ji
= mono_jit_info_table_find_internal (domain
, ip
, FALSE
);
105 return sgen_is_critical_method (mono_jit_info_get_method (ji
));
109 restart_threads_until_none_in_managed_allocator (void)
111 SgenThreadInfo
*info
;
112 int num_threads_died
= 0;
113 int sleep_duration
= -1;
116 int restart_count
= 0, restarted_count
= 0;
117 /* restart all threads that stopped in the
119 FOREACH_THREAD_SAFE (info
) {
121 if (info
->skip
|| info
->gc_disabled
)
123 if (mono_thread_info_run_state (info
) == STATE_RUNNING
&& (!info
->stack_start
|| info
->in_critical_region
|| info
->info
.inside_critical_region
||
124 is_ip_in_managed_allocator (info
->stopped_domain
, info
->stopped_ip
))) {
125 binary_protocol_thread_restart ((gpointer
)mono_thread_info_get_tid (info
));
126 SGEN_LOG (3, "thread %p resumed.", (void*) (size_t) info
->info
.native_handle
);
127 result
= sgen_resume_thread (info
);
134 /* we set the stopped_ip to
135 NULL for threads which
136 we're not restarting so
137 that we can easily identify
139 info
->stopped_ip
= NULL
;
140 info
->stopped_domain
= NULL
;
142 } END_FOREACH_THREAD_SAFE
143 /* if no threads were restarted, we're done */
144 if (restart_count
== 0)
147 /* wait for the threads to signal their restart */
148 sgen_wait_for_suspend_ack (restart_count
);
150 if (sleep_duration
< 0) {
151 mono_thread_info_yield ();
154 g_usleep (sleep_duration
);
155 sleep_duration
+= 10;
158 /* stop them again */
159 FOREACH_THREAD (info
) {
161 if (info
->skip
|| info
->stopped_ip
== NULL
)
163 result
= sgen_suspend_thread (info
);
171 /* some threads might have died */
172 num_threads_died
+= restart_count
- restarted_count
;
173 /* wait for the threads to signal their suspension
175 sgen_wait_for_suspend_ack (restarted_count
);
178 return num_threads_died
;
182 acquire_gc_locks (void)
185 mono_thread_info_suspend_lock ();
189 release_gc_locks (void)
191 mono_thread_info_suspend_unlock ();
196 count_cards (long long *major_total
, long long *major_marked
, long long *los_total
, long long *los_marked
)
198 sgen_get_major_collector ()->count_cards (major_total
, major_marked
);
199 sgen_los_count_cards (los_total
, los_marked
);
202 static TV_DECLARE (stop_world_time
);
203 static unsigned long max_pause_usec
= 0;
205 static long long time_stop_world
;
206 static long long time_restart_world
;
208 /* LOCKING: assumes the GC lock is held */
210 sgen_stop_world (int generation
)
212 TV_DECLARE (end_handshake
);
215 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD
, generation
);
216 MONO_GC_WORLD_STOP_BEGIN ();
217 binary_protocol_world_stopping (sgen_timestamp ());
220 /* We start to scan after locks are taking, this ensures we won't be interrupted. */
221 sgen_process_togglerefs ();
223 update_current_thread_stack (&count
);
225 sgen_global_stop_count
++;
226 SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count
, mono_thread_info_current (), (gpointer
)mono_native_thread_id_get ());
227 TV_GETTIME (stop_world_time
);
228 count
= sgen_thread_handshake (TRUE
);
229 dead
= restart_threads_until_none_in_managed_allocator ();
231 g_error ("More threads have died (%d) that been initialy suspended %d", dead
, count
);
234 SGEN_LOG (3, "world stopped %d thread(s)", count
);
235 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD
, generation
);
236 MONO_GC_WORLD_STOP_END ();
237 if (binary_protocol_is_enabled ()) {
238 long long major_total
= -1, major_marked
= -1, los_total
= -1, los_marked
= -1;
239 if (binary_protocol_is_heavy_enabled ())
240 count_cards (&major_total
, &major_marked
, &los_total
, &los_marked
);
241 binary_protocol_world_stopped (sgen_timestamp (), major_total
, major_marked
, los_total
, los_marked
);
244 TV_GETTIME (end_handshake
);
245 time_stop_world
+= TV_ELAPSED (stop_world_time
, end_handshake
);
247 sgen_memgov_collection_start (generation
);
248 if (sgen_need_bridge_processing ())
249 sgen_bridge_reset_data ();
254 /* LOCKING: assumes the GC lock is held */
256 sgen_restart_world (int generation
, GGTimingInfo
*timing
)
259 SgenThreadInfo
*info
;
261 TV_DECLARE (start_handshake
);
262 TV_DECLARE (end_bridge
);
263 unsigned long usec
, bridge_usec
;
265 if (binary_protocol_is_enabled ()) {
266 long long major_total
= -1, major_marked
= -1, los_total
= -1, los_marked
= -1;
267 if (binary_protocol_is_heavy_enabled ())
268 count_cards (&major_total
, &major_marked
, &los_total
, &los_marked
);
269 binary_protocol_world_restarting (generation
, sgen_timestamp (), major_total
, major_marked
, los_total
, los_marked
);
272 /* notify the profiler of the leftovers */
273 /* FIXME this is the wrong spot at we can STW for non collection reasons. */
274 if (G_UNLIKELY (mono_profiler_events
& MONO_PROFILE_GC_MOVES
))
275 sgen_gc_event_moves ();
276 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD
, generation
);
277 MONO_GC_WORLD_RESTART_BEGIN (generation
);
278 FOREACH_THREAD (info
) {
279 info
->stack_start
= NULL
;
281 memset (&info
->ctx
, 0, sizeof (MonoContext
));
283 memset (&info
->regs
, 0, sizeof (info
->regs
));
287 TV_GETTIME (start_handshake
);
288 count
= sgen_thread_handshake (FALSE
);
290 time_restart_world
+= TV_ELAPSED (start_handshake
, end_sw
);
291 usec
= TV_ELAPSED (stop_world_time
, end_sw
);
292 max_pause_usec
= MAX (usec
, max_pause_usec
);
293 SGEN_LOG (2, "restarted %d thread(s) (pause time: %d usec, max: %d)", count
, (int)usec
, (int)max_pause_usec
);
294 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD
, generation
);
295 MONO_GC_WORLD_RESTART_END (generation
);
296 binary_protocol_world_restarted (generation
, sgen_timestamp ());
299 * We must release the thread info suspend lock after doing
300 * the thread handshake. Otherwise, if the GC stops the world
301 * and a thread is in the process of starting up, but has not
302 * yet registered (it's not in the thread_list), it is
303 * possible that the thread does register while the world is
304 * stopped. When restarting the GC will then try to restart
305 * said thread, but since it never got the suspend signal, it
306 * cannot answer the restart signal, so a deadlock results.
310 sgen_try_free_some_memory
= TRUE
;
312 if (sgen_need_bridge_processing ())
313 sgen_bridge_processing_finish (generation
);
315 TV_GETTIME (end_bridge
);
316 bridge_usec
= TV_ELAPSED (end_sw
, end_bridge
);
319 timing
[0].stw_time
= usec
;
320 timing
[0].bridge_time
= bridge_usec
;
323 sgen_memgov_collection_end (generation
, timing
, timing
? 2 : 0);
331 mono_counters_register ("World stop", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &time_stop_world
);
332 mono_counters_register ("World restart", MONO_COUNTER_GC
| MONO_COUNTER_LONG
| MONO_COUNTER_TIME
, &time_restart_world
);