Merge pull request #487 from mayerwin/patch-1
[mono.git] / mono / metadata / sgen-stw.c
1 /*
2  * sgen-stw.c: Stop the world functionality
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *  Rodrigo Kumpera (kumpera@gmail.com)
7  *
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
12  *
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;
16  *
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.
21  *
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.
25  */
26
27 #include "config.h"
28 #ifdef HAVE_SGEN_GC
29
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
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
41
42 inline static void*
43 align_pointer (void *ptr)
44 {
45         mword p = (mword)ptr;
46         p += sizeof (gpointer) - 1;
47         p &= ~ (sizeof (gpointer) - 1);
48         return (void*)p;
49 }
50
51 #ifdef USE_MONO_CTX
52 static MonoContext cur_thread_ctx = {0};
53 #else
54 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
55 #endif
56
57 static void
58 update_current_thread_stack (void *start)
59 {
60         int stack_guard = 0;
61 #ifndef USE_MONO_CTX
62         void *reg_ptr = cur_thread_regs;
63 #endif
64         SgenThreadInfo *info = mono_thread_info_current ();
65         
66         info->stack_start = align_pointer (&stack_guard);
67         g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
68 #ifdef USE_MONO_CTX
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);
73 #else
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);
78 #endif
79 }
80
81 static gboolean
82 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
83 {
84         MonoJitInfo *ji;
85
86         if (!mono_thread_internal_current ())
87                 /* Happens during thread attach */
88                 return FALSE;
89
90         if (!ip || !domain)
91                 return FALSE;
92         if (!sgen_has_critical_method ())
93                 return FALSE;
94         ji = mono_jit_info_table_find (domain, ip);
95         if (!ji)
96                 return FALSE;
97
98         return sgen_is_critical_method (ji->method);
99 }
100
101 static int
102 restart_threads_until_none_in_managed_allocator (void)
103 {
104         SgenThreadInfo *info;
105         int num_threads_died = 0;
106         int sleep_duration = -1;
107
108         for (;;) {
109                 int restart_count = 0, restarted_count = 0;
110                 /* restart all threads that stopped in the
111                    allocator */
112                 FOREACH_THREAD_SAFE (info) {
113                         gboolean result;
114                         if (info->skip || info->gc_disabled || !info->joined_stw)
115                                 continue;
116                         if (!info->thread_is_dying && (!info->stack_start || info->in_critical_region ||
117                                         is_ip_in_managed_allocator (info->stopped_domain, info->stopped_ip))) {
118                                 binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info));
119                                 SGEN_LOG (3, "thread %p resumed.", (void*)info->info.native_handle);
120                                 result = sgen_resume_thread (info);
121                                 if (result) {
122                                         ++restart_count;
123                                 } else {
124                                         info->skip = 1;
125                                 }
126                         } else {
127                                 /* we set the stopped_ip to
128                                    NULL for threads which
129                                    we're not restarting so
130                                    that we can easily identify
131                                    the others */
132                                 info->stopped_ip = NULL;
133                                 info->stopped_domain = NULL;
134                         }
135                 } END_FOREACH_THREAD_SAFE
136                 /* if no threads were restarted, we're done */
137                 if (restart_count == 0)
138                         break;
139
140                 /* wait for the threads to signal their restart */
141                 sgen_wait_for_suspend_ack (restart_count);
142
143                 if (sleep_duration < 0) {
144 #ifdef HOST_WIN32
145                         SwitchToThread ();
146 #else
147                         sched_yield ();
148 #endif
149                         sleep_duration = 0;
150                 } else {
151                         g_usleep (sleep_duration);
152                         sleep_duration += 10;
153                 }
154
155                 /* stop them again */
156                 FOREACH_THREAD (info) {
157                         gboolean result;
158                         if (info->skip || info->stopped_ip == NULL)
159                                 continue;
160                         result = sgen_suspend_thread (info);
161
162                         if (result) {
163                                 ++restarted_count;
164                         } else {
165                                 info->skip = 1;
166                         }
167                 } END_FOREACH_THREAD
168                 /* some threads might have died */
169                 num_threads_died += restart_count - restarted_count;
170                 /* wait for the threads to signal their suspension
171                    again */
172                 sgen_wait_for_suspend_ack (restarted_count);
173         }
174
175         return num_threads_died;
176 }
177
178 static void
179 acquire_gc_locks (void)
180 {
181         LOCK_INTERRUPTION;
182         mono_thread_info_suspend_lock ();
183 }
184
185 static void
186 release_gc_locks (void)
187 {
188         mono_thread_info_suspend_unlock ();
189         UNLOCK_INTERRUPTION;
190 }
191
192 static void
193 stw_bridge_process (void)
194 {
195         sgen_bridge_processing_stw_step ();
196 }
197
198 static void
199 bridge_process (int generation)
200 {
201         sgen_bridge_processing_finish (generation);
202 }
203
204 static TV_DECLARE (stop_world_time);
205 static unsigned long max_pause_usec = 0;
206
207 /* LOCKING: assumes the GC lock is held */
208 int
209 sgen_stop_world (int generation)
210 {
211         int count, dead;
212
213         /*XXX this is the right stop, thought might not be the nicest place to put it*/
214         sgen_process_togglerefs ();
215
216         mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
217         acquire_gc_locks ();
218
219         update_current_thread_stack (&count);
220
221         sgen_global_stop_count++;
222         SGEN_LOG (3, "stopping world n %d from %p %p", sgen_global_stop_count, mono_thread_info_current (), (gpointer)mono_native_thread_id_get ());
223         TV_GETTIME (stop_world_time);
224         count = sgen_thread_handshake (TRUE);
225         dead = restart_threads_until_none_in_managed_allocator ();
226         if (count < dead)
227                 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
228         count -= dead;
229
230         SGEN_LOG (3, "world stopped %d thread(s)", count);
231         mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
232
233         sgen_memgov_collection_start (generation);
234
235         return count;
236 }
237
238 /* LOCKING: assumes the GC lock is held */
239 int
240 sgen_restart_world (int generation, GGTimingInfo *timing)
241 {
242         int count;
243         SgenThreadInfo *info;
244         TV_DECLARE (end_sw);
245         TV_DECLARE (end_bridge);
246         unsigned long usec, bridge_usec;
247
248         /* notify the profiler of the leftovers */
249         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES))
250                 sgen_gc_event_moves ();
251         mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
252         FOREACH_THREAD (info) {
253                 info->stack_start = NULL;
254 #ifdef USE_MONO_CTX
255                 memset (&info->ctx, 0, sizeof (MonoContext));
256 #else
257                 memset (&info->regs, 0, sizeof (info->regs));
258 #endif
259         } END_FOREACH_THREAD
260
261         stw_bridge_process ();
262         release_gc_locks ();
263
264         count = sgen_thread_handshake (FALSE);
265         TV_GETTIME (end_sw);
266         usec = TV_ELAPSED (stop_world_time, end_sw);
267         max_pause_usec = MAX (usec, max_pause_usec);
268         SGEN_LOG (2, "restarted %d thread(s) (pause time: %d usec, max: %d)", count, (int)usec, (int)max_pause_usec);
269         mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
270
271         bridge_process (generation);
272
273         TV_GETTIME (end_bridge);
274         bridge_usec = TV_ELAPSED (end_sw, end_bridge);
275
276         if (timing) {
277                 timing [0].stw_time = usec;
278                 timing [0].bridge_time = bridge_usec;
279         }
280         
281         sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
282
283         return count;
284 }
285
286 #endif