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.
12 * Permission is hereby granted, free of charge, to any person obtaining
13 * a copy of this software and associated documentation files (the
14 * "Software"), to deal in the Software without restriction, including
15 * without limitation the rights to use, copy, modify, merge, publish,
16 * distribute, sublicense, and/or sell copies of the Software, and to
17 * permit persons to whom the Software is furnished to do so, subject to
18 * the following conditions:
20 * The above copyright notice and this permission notice shall be
21 * included in all copies or substantial portions of the Software.
23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 #include "metadata/sgen-gc.h"
36 #include "metadata/sgen-protocol.h"
37 #include "metadata/sgen-memory-governor.h"
38 #include "metadata/profiler-private.h"
39 #include "utils/mono-time.h"
40 #include "utils/dtrace.h"
42 #define TV_DECLARE SGEN_TV_DECLARE
43 #define TV_GETTIME SGEN_TV_GETTIME
44 #define TV_ELAPSED SGEN_TV_ELAPSED
45 #define TV_ELAPSED_MS SGEN_TV_ELAPSED_MS
48 align_pointer (void *ptr)
51 p += sizeof (gpointer) - 1;
52 p &= ~ (sizeof (gpointer) - 1);
57 static MonoContext cur_thread_ctx = {0};
59 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
63 update_current_thread_stack (void *start)
67 void *reg_ptr = cur_thread_regs;
69 SgenThreadInfo *info = mono_thread_info_current ();
71 info->stack_start = align_pointer (&stack_guard);
72 g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
74 MONO_CONTEXT_GET_CURRENT (cur_thread_ctx);
75 memcpy (&info->ctx, &cur_thread_ctx, sizeof (MonoContext));
76 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
77 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, NULL, &info->ctx);
79 ARCH_STORE_REGS (reg_ptr);
80 memcpy (&info->regs, reg_ptr, sizeof (info->regs));
81 if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
82 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, NULL, NULL);
87 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
91 if (!mono_thread_internal_current ())
92 /* Happens during thread attach */
97 if (!sgen_has_critical_method ())
99 ji = mono_jit_info_table_find (domain, ip);
103 return sgen_is_critical_method (ji->method);
107 restart_threads_until_none_in_managed_allocator (void)
109 SgenThreadInfo *info;
110 int num_threads_died = 0;
111 int sleep_duration = -1;
114 int restart_count = 0, restarted_count = 0;
115 /* restart all threads that stopped in the
117 FOREACH_THREAD_SAFE (info) {
119 if (info->skip || info->gc_disabled || !info->joined_stw)
121 if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
122 is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
123 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
124 SGEN_LOG (3, "thread %p resumed.", (void*)info->info.native_handle);
125 result = sgen_resume_thread (info);
132 /* we set the stopped_ip to
133 NULL for threads which
134 we're not restarting so
135 that we can easily identify
137 info->stopped_ip = NULL;
138 info->stopped_domain = NULL;
140 } END_FOREACH_THREAD_SAFE
141 /* if no threads were restarted, we're done */
142 if (restart_count == 0)
145 /* wait for the threads to signal their restart */
146 sgen_wait_for_suspend_ack (restart_count);
148 if (sleep_duration < 0) {
156 g_usleep (sleep_duration);
157 sleep_duration += 10;
160 /* stop them again */
161 FOREACH_THREAD (info) {
163 if (info->skip || info->stopped_ip == NULL)
165 result = sgen_suspend_thread (info);
173 /* some threads might have died */
174 num_threads_died += restart_count - restarted_count;
175 /* wait for the threads to signal their suspension
177 sgen_wait_for_suspend_ack (restarted_count);
180 return num_threads_died;
184 acquire_gc_locks (void)
187 mono_thread_info_suspend_lock ();
191 release_gc_locks (void)
193 mono_thread_info_suspend_unlock ();
198 stw_bridge_process (void)
200 sgen_bridge_processing_stw_step ();
204 bridge_process (int generation)
206 sgen_bridge_processing_finish (generation);
209 static TV_DECLARE (stop_world_time);
210 static unsigned long max_pause_usec = 0;
212 /* LOCKING: assumes the GC lock is held */
214 sgen_stop_world (int generation)
218 /*XXX this is the right stop, thought might not be the nicest place to put it*/
219 sgen_process_togglerefs ();
221 mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
224 update_current_thread_stack (&count);
226 sgen_global_stop_count++;
227 SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ());
228 TV_GETTIME (stop_world_time);
229 count = sgen_thread_handshake (TRUE);
230 dead = restart_threads_until_none_in_managed_allocator ();
232 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
235 SGEN_LOG (3, "world stopped %d thread(s)", count);
236 mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
238 sgen_memgov_collection_start (generation);
243 /* LOCKING: assumes the GC lock is held */
245 sgen_restart_world (int generation, GGTimingInfo *timing)
248 SgenThreadInfo *info;
250 TV_DECLARE (end_bridge);
251 unsigned long usec, bridge_usec;
253 /* notify the profiler of the leftovers */
254 if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES))
255 sgen_gc_event_moves ();
256 mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
257 FOREACH_THREAD (info) {
258 info->stack_start = NULL;
260 memset (&info->ctx, 0, sizeof (MonoContext));
262 memset (&info->regs, 0, sizeof (info->regs));
266 stw_bridge_process ();
269 count = sgen_thread_handshake (FALSE);
271 usec = TV_ELAPSED (stop_world_time, end_sw);
272 max_pause_usec = MAX (usec, max_pause_usec);
273 SGEN_LOG (2, "restarted %d thread(s) (pause time: %d usec, max: %d)", count, (int)usec, (int)max_pause_usec);
274 mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
276 bridge_process (generation);
278 TV_GETTIME (end_bridge);
279 bridge_usec = TV_ELAPSED (end_sw, end_bridge);
282 timing [0].stw_time = usec;
283 timing [0].bridge_time = bridge_usec;
286 sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);