Add suspend done everywhere else.
[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 #include "utils/mono-counters.h"
37
38 #define TV_DECLARE SGEN_TV_DECLARE
39 #define TV_GETTIME SGEN_TV_GETTIME
40 #define TV_ELAPSED SGEN_TV_ELAPSED
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;
53 #else
54 static mword cur_thread_regs [ARCH_NUM_REGS];
55 #endif
56
57 static void
58 update_current_thread_stack (void *start)
59 {
60         int stack_guard = 0;
61 #if !defined(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
95         /*
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.
99          */
100         ji = mono_jit_info_table_find_internal (domain, ip, FALSE);
101         if (!ji)
102                 return FALSE;
103
104         return sgen_is_critical_method (mono_jit_info_get_method (ji));
105 }
106
107 static int
108 restart_threads_until_none_in_managed_allocator (void)
109 {
110         SgenThreadInfo *info;
111         int num_threads_died = 0;
112         int sleep_duration = -1;
113
114         for (;;) {
115                 int restart_count = 0, restarted_count = 0;
116                 /* restart all threads that stopped in the
117                    allocator */
118                 FOREACH_THREAD_SAFE (info) {
119                         gboolean result;
120                         if (info->skip || info->gc_disabled || info->suspend_done)
121                                 continue;
122                         if (mono_thread_info_is_live (info) && (!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);
127                                 if (result) {
128                                         ++restart_count;
129                                 } else {
130                                         info->skip = 1;
131                                 }
132                         } else {
133                                 /* we set the stopped_ip to
134                                    NULL for threads which
135                                    we're not restarting so
136                                    that we can easily identify
137                                    the others */
138                                 info->stopped_ip = NULL;
139                                 info->stopped_domain = NULL;
140                                 info->suspend_done = TRUE;
141                         }
142                 } END_FOREACH_THREAD_SAFE
143                 /* if no threads were restarted, we're done */
144                 if (restart_count == 0)
145                         break;
146
147                 /* wait for the threads to signal their restart */
148                 sgen_wait_for_suspend_ack (restart_count);
149
150                 if (sleep_duration < 0) {
151                         mono_thread_info_yield ();
152                         sleep_duration = 0;
153                 } else {
154                         g_usleep (sleep_duration);
155                         sleep_duration += 10;
156                 }
157
158                 /* stop them again */
159                 FOREACH_THREAD (info) {
160                         gboolean result;
161                         if (info->skip || info->stopped_ip == NULL)
162                                 continue;
163                         result = sgen_suspend_thread (info);
164
165                         if (result) {
166                                 ++restarted_count;
167                         } else {
168                                 info->skip = 1;
169                         }
170                 } END_FOREACH_THREAD
171                 /* some threads might have died */
172                 num_threads_died += restart_count - restarted_count;
173                 /* wait for the threads to signal their suspension
174                    again */
175                 sgen_wait_for_suspend_ack (restarted_count);
176         }
177
178         return num_threads_died;
179 }
180
181 static void
182 acquire_gc_locks (void)
183 {
184         LOCK_INTERRUPTION;
185         mono_thread_info_suspend_lock ();
186 }
187
188 static void
189 release_gc_locks (void)
190 {
191         mono_thread_info_suspend_unlock ();
192         UNLOCK_INTERRUPTION;
193 }
194
195 static void
196 count_cards (long long *major_total, long long *major_marked, long long *los_total, long long *los_marked)
197 {
198         sgen_get_major_collector ()->count_cards (major_total, major_marked);
199         sgen_los_count_cards (los_total, los_marked);
200 }
201
202 static TV_DECLARE (stop_world_time);
203 static unsigned long max_pause_usec = 0;
204
205 static guint64 time_stop_world;
206 static guint64 time_restart_world;
207
208 /* LOCKING: assumes the GC lock is held */
209 int
210 sgen_stop_world (int generation)
211 {
212         TV_DECLARE (end_handshake);
213         int count, dead;
214
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 ());
218         acquire_gc_locks ();
219
220         /* We start to scan after locks are taking, this ensures we won't be interrupted. */
221         sgen_process_togglerefs ();
222
223         update_current_thread_stack (&count);
224
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 ();
230         if (count < dead)
231                 g_error ("More threads have died (%d) that been initialy suspended %d", dead, count);
232         count -= dead;
233
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);
242         }
243
244         TV_GETTIME (end_handshake);
245         time_stop_world += TV_ELAPSED (stop_world_time, end_handshake);
246
247         sgen_memgov_collection_start (generation);
248         if (sgen_need_bridge_processing ())
249                 sgen_bridge_reset_data ();
250
251         return count;
252 }
253
254 /* LOCKING: assumes the GC lock is held */
255 int
256 sgen_restart_world (int generation, GGTimingInfo *timing)
257 {
258         int count;
259         SgenThreadInfo *info;
260         TV_DECLARE (end_sw);
261         TV_DECLARE (start_handshake);
262         TV_DECLARE (end_bridge);
263         unsigned long usec, bridge_usec;
264
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);
270         }
271
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;
280 #ifdef USE_MONO_CTX
281                 memset (&info->ctx, 0, sizeof (MonoContext));
282 #else
283                 memset (&info->regs, 0, sizeof (info->regs));
284 #endif
285         } END_FOREACH_THREAD
286
287         TV_GETTIME (start_handshake);
288         count = sgen_thread_handshake (FALSE);
289         TV_GETTIME (end_sw);
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 ());
297
298         /*
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.
307          */
308         release_gc_locks ();
309
310         sgen_try_free_some_memory = TRUE;
311
312         if (sgen_need_bridge_processing ())
313                 sgen_bridge_processing_finish (generation);
314
315         TV_GETTIME (end_bridge);
316         bridge_usec = TV_ELAPSED (end_sw, end_bridge);
317
318         if (timing) {
319                 timing [0].stw_time = usec;
320                 timing [0].bridge_time = bridge_usec;
321         }
322         
323         sgen_memgov_collection_end (generation, timing, timing ? 2 : 0);
324
325         return count;
326 }
327
328 void
329 sgen_init_stw (void)
330 {
331         mono_counters_register ("World stop", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_stop_world);
332         mono_counters_register ("World restart", MONO_COUNTER_GC | MONO_COUNTER_ULONG | MONO_COUNTER_TIME, &time_restart_world);
333 }
334
335 #endif