Merge pull request #409 from Alkarex/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  *
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:
19  * 
20  * The above copyright notice and this permission notice shall be
21  * included in all copies or substantial portions of the Software.
22  * 
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.
30  */
31
32 #include "config.h"
33 #ifdef HAVE_SGEN_GC
34
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"
41
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
46
47 inline static void*
48 align_pointer (void *ptr)
49 {
50         mword p = (mword)ptr;
51         p += sizeof (gpointer) - 1;
52         p &= ~ (sizeof (gpointer) - 1);
53         return (void*)p;
54 }
55
56 #ifdef USE_MONO_CTX
57 static MonoContext cur_thread_ctx = {0};
58 #else
59 static mword cur_thread_regs [ARCH_NUM_REGS] = {0};
60 #endif
61
62 static void
63 update_current_thread_stack (void *start)
64 {
65         int stack_guard = 0;
66 #ifndef USE_MONO_CTX
67         void *reg_ptr = cur_thread_regs;
68 #endif
69         SgenThreadInfo *info = mono_thread_info_current ();
70         
71         info->stack_start = align_pointer (&stack_guard);
72         g_assert (info->stack_start >= info->stack_start_limit && info->stack_start < info->stack_end);
73 #ifdef USE_MONO_CTX
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);
78 #else
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);
83 #endif
84 }
85
86 static gboolean
87 is_ip_in_managed_allocator (MonoDomain *domain, gpointer ip)
88 {
89         MonoJitInfo *ji;
90
91         if (!mono_thread_internal_current ())
92                 /* Happens during thread attach */
93                 return FALSE;
94
95         if (!ip || !domain)
96                 return FALSE;
97         if (!sgen_has_critical_method ())
98                 return FALSE;
99         ji = mono_jit_info_table_find (domain, ip);
100         if (!ji)
101                 return FALSE;
102
103         return sgen_is_critical_method (ji->method);
104 }
105
106 static int
107 restart_threads_until_none_in_managed_allocator (void)
108 {
109         SgenThreadInfo *info;
110         int num_threads_died = 0;
111         int sleep_duration = -1;
112
113         for (;;) {
114                 int restart_count = 0, restarted_count = 0;
115                 /* restart all threads that stopped in the
116                    allocator */
117                 FOREACH_THREAD_SAFE (info) {
118                         gboolean result;
119                         if (info->skip || info->gc_disabled || !info->joined_stw)
120                                 continue;
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);
126                                 if (result) {
127                                         ++restart_count;
128                                 } else {
129                                         info->skip = 1;
130                                 }
131                         } else {
132                                 /* we set the stopped_ip to
133                                    NULL for threads which
134                                    we're not restarting so
135                                    that we can easily identify
136                                    the others */
137                                 info->stopped_ip = NULL;
138                                 info->stopped_domain = NULL;
139                         }
140                 } END_FOREACH_THREAD_SAFE
141                 /* if no threads were restarted, we're done */
142                 if (restart_count == 0)
143                         break;
144
145                 /* wait for the threads to signal their restart */
146                 sgen_wait_for_suspend_ack (restart_count);
147
148                 if (sleep_duration < 0) {
149 #ifdef HOST_WIN32
150                         SwitchToThread ();
151 #else
152                         sched_yield ();
153 #endif
154                         sleep_duration = 0;
155                 } else {
156                         g_usleep (sleep_duration);
157                         sleep_duration += 10;
158                 }
159
160                 /* stop them again */
161                 FOREACH_THREAD (info) {
162                         gboolean result;
163                         if (info->skip || info->stopped_ip == NULL)
164                                 continue;
165                         result = sgen_suspend_thread (info);
166
167                         if (result) {
168                                 ++restarted_count;
169                         } else {
170                                 info->skip = 1;
171                         }
172                 } END_FOREACH_THREAD
173                 /* some threads might have died */
174                 num_threads_died += restart_count - restarted_count;
175                 /* wait for the threads to signal their suspension
176                    again */
177                 sgen_wait_for_suspend_ack (restarted_count);
178         }
179
180         return num_threads_died;
181 }
182
183 static void
184 acquire_gc_locks (void)
185 {
186         LOCK_INTERRUPTION;
187         mono_thread_info_suspend_lock ();
188 }
189
190 static void
191 release_gc_locks (void)
192 {
193         mono_thread_info_suspend_unlock ();
194         UNLOCK_INTERRUPTION;
195 }
196
197 static void
198 stw_bridge_process (void)
199 {
200         sgen_bridge_processing_stw_step ();
201 }
202
203 static void
204 bridge_process (int generation)
205 {
206         sgen_bridge_processing_finish (generation);
207 }
208
209 static TV_DECLARE (stop_world_time);
210 static unsigned long max_pause_usec = 0;
211
212 /* LOCKING: assumes the GC lock is held */
213 int
214 sgen_stop_world (int generation)
215 {
216         int count, dead;
217
218         /*XXX this is the right stop, thought might not be the nicest place to put it*/
219         sgen_process_togglerefs ();
220
221         mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
222         acquire_gc_locks ();
223
224         update_current_thread_stack (&count);
225
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 ();
231         if (count < dead)
232                 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
233         count -= dead;
234
235         SGEN_LOG (3, "world stopped %d thread(s)", count);
236         mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
237
238         sgen_memgov_collection_start (generation);
239
240         return count;
241 }
242
243 /* LOCKING: assumes the GC lock is held */
244 int
245 sgen_restart_world (int generation, GGTimingInfo *timing)
246 {
247         int count;
248         SgenThreadInfo *info;
249         TV_DECLARE (end_sw);
250         TV_DECLARE (end_bridge);
251         unsigned long usec, bridge_usec;
252
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;
259 #ifdef USE_MONO_CTX
260                 memset (&info->ctx, 0, sizeof (MonoContext));
261 #else
262                 memset (&info->regs, 0, sizeof (info->regs));
263 #endif
264         } END_FOREACH_THREAD
265
266         stw_bridge_process ();
267         release_gc_locks ();
268
269         count = sgen_thread_handshake (FALSE);
270         TV_GETTIME (end_sw);
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);
275
276         bridge_process (generation);
277
278         TV_GETTIME (end_bridge);
279         bridge_usec = TV_ELAPSED (end_sw, end_bridge);
280
281         if (timing) {
282                 timing [0].stw_time = usec;
283                 timing [0].bridge_time = bridge_usec;
284         }
285         
286         sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
287
288         return count;
289 }
290
291 #endif