37979a497d40939f04fe9a03de7d307f564efbb2
[mono.git] / mono / metadata / sgen-os-posix.c
1 /*
2  * sgen-os-posix.c: Posix support.
3  *
4  * Author:
5  *      Paolo Molaro (lupus@ximian.com)
6  *      Mark Probst (mprobst@novell.com)
7  *      Geoff Norton (gnorton@novell.com)
8  *
9  * Copyright 2010 Novell, Inc (http://www.novell.com)
10  * Copyright (C) 2012 Xamarin Inc
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Library General Public
14  * License 2.0 as published by the Free Software Foundation;
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Library General Public License for more details.
20  *
21  * You should have received a copy of the GNU Library General Public
22  * License 2.0 along with this library; if not, write to the Free
23  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  */
25
26 #include "config.h"
27
28 #if defined(HAVE_SGEN_GC) && !defined(USE_COOP_GC)
29 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
30
31 #include <errno.h>
32 #include <glib.h>
33 #include "sgen/sgen-gc.h"
34 #include "metadata/gc-internals.h"
35 #include "sgen/sgen-archdep.h"
36 #include "metadata/object-internals.h"
37 #include "utils/mono-signal-handler.h"
38
39 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
40 const static int suspend_signal_num = SIGXFSZ;
41 #else
42 const static int suspend_signal_num = SIGPWR;
43 #endif
44 const static int restart_signal_num = SIGXCPU;
45
46 static SgenSemaphore suspend_ack_semaphore;
47 static SgenSemaphore *suspend_ack_semaphore_ptr;
48
49 static sigset_t suspend_signal_mask;
50 static sigset_t suspend_ack_signal_mask;
51
52 static void
53 suspend_thread (SgenThreadInfo *info, void *context)
54 {
55         int stop_count;
56 #ifndef USE_MONO_CTX
57         gpointer regs [ARCH_NUM_REGS];
58 #endif
59         MonoContext ctx;
60         gpointer stack_start;
61
62         info->client_info.stopped_domain = mono_domain_get ();
63         info->client_info.signal = 0;
64         stop_count = sgen_global_stop_count;
65         /* duplicate signal */
66         if (0 && info->client_info.stop_count == stop_count)
67                 return;
68
69 #ifdef USE_MONO_CTX
70         if (context) {
71                 mono_sigctx_to_monoctx (context, &ctx);
72                 info->client_info.stopped_ip = MONO_CONTEXT_GET_IP (&ctx);
73                 stack_start = (((guint8 *) MONO_CONTEXT_GET_SP (&ctx)) - REDZONE_SIZE);
74         } else {
75                 info->client_info.stopped_ip = NULL;
76                 stack_start = NULL;
77         }
78 #else
79         info->client_info.stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
80         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
81 #endif
82
83         /* If stack_start is not within the limits, then don't set it
84            in info and we will be restarted. */
85         if (stack_start >= info->client_info.stack_start_limit && stack_start <= info->client_info.stack_end) {
86                 info->client_info.stack_start = stack_start;
87
88 #ifdef USE_MONO_CTX
89                 if (context) {
90                         memcpy (&info->client_info.ctx, &ctx, sizeof (MonoContext));
91                 } else {
92                         memset (&info->client_info.ctx, 0, sizeof (MonoContext));
93                 }
94 #else
95                 if (context) {
96                         ARCH_COPY_SIGCTX_REGS (regs, context);
97                         memcpy (&info->client_info.regs, regs, sizeof (info->client_info.regs));
98                 } else {
99                         memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
100                 }
101 #endif
102         } else {
103                 g_assert (!info->client_info.stack_start);
104         }
105
106         /* Notify the JIT */
107         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
108                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->client_info.runtime_data, context, NULL);
109
110         SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer) (gsize) mono_native_thread_id_get ());
111
112         /*
113         Block the restart signal. 
114         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
115         which might miss the signal and get stuck.
116         */
117         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
118
119         /* notify the waiting thread */
120         SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr);
121         info->client_info.stop_count = stop_count;
122
123         /* wait until we receive the restart signal */
124         do {
125                 info->client_info.signal = 0;
126                 sigsuspend (&suspend_signal_mask);
127         } while (info->client_info.signal != restart_signal_num);
128
129         /* Unblock the restart signal. */
130         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
131
132         SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer) (gsize) mono_native_thread_id_get ());
133         /* notify the waiting thread */
134         SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr);
135 }
136
137 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
138 MONO_SIG_HANDLER_FUNC (static, suspend_handler)
139 {
140         /*
141          * The suspend signal handler potentially uses syscalls that
142          * can set errno, and it calls functions that use the hazard
143          * pointer machinery.  Since we're interrupting other code we
144          * must restore those to the values they had when we
145          * interrupted.
146          */
147         SgenThreadInfo *info;
148         int old_errno = errno;
149         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
150         MONO_SIG_HANDLER_GET_CONTEXT;
151
152         info = mono_thread_info_current ();
153         suspend_thread (info, ctx);
154
155         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
156         errno = old_errno;
157 }
158
159 MONO_SIG_HANDLER_FUNC (static, restart_handler)
160 {
161         SgenThreadInfo *info;
162         int old_errno = errno;
163
164         info = mono_thread_info_current ();
165         info->client_info.signal = restart_signal_num;
166         SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer) (gsize) mono_native_thread_id_get ());
167         errno = old_errno;
168 }
169
170 gboolean
171 sgen_resume_thread (SgenThreadInfo *info)
172 {
173         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
174 }
175
176 gboolean
177 sgen_suspend_thread (SgenThreadInfo *info)
178 {
179         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
180 }
181
182 void
183 sgen_wait_for_suspend_ack (int count)
184 {
185         int i, result;
186
187         for (i = 0; i < count; ++i) {
188                 while ((result = SGEN_SEMAPHORE_WAIT (suspend_ack_semaphore_ptr)) != 0) {
189                         if (errno != EINTR) {
190                                 g_error ("SGEN_SEMAPHORE_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
191                         }
192                 }
193         }
194 }
195
196 int
197 sgen_thread_handshake (BOOL suspend)
198 {
199         int count, result;
200         int signum = suspend ? suspend_signal_num : restart_signal_num;
201
202         MonoNativeThreadId me = mono_native_thread_id_get ();
203
204         count = 0;
205         mono_thread_info_current ()->client_info.suspend_done = TRUE;
206         FOREACH_THREAD (info) {
207                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
208                         continue;
209                 }
210                 info->client_info.suspend_done = FALSE;
211                 if (info->client_info.gc_disabled)
212                         continue;
213                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
214                         continue;*/
215                 result = mono_threads_pthread_kill (info, signum);
216                 if (result == 0) {
217                         count++;
218                 } else {
219                         info->client_info.skip = 1;
220                 }
221         } FOREACH_THREAD_END
222
223         sgen_wait_for_suspend_ack (count);
224
225         SGEN_LOG (4, "%s handshake for %d threads\n", suspend ? "suspend" : "resume", count);
226
227         return count;
228 }
229
230 void
231 sgen_os_init (void)
232 {
233         struct sigaction sinfo;
234
235         if (mono_thread_info_unified_management_enabled ())
236                 return;
237
238         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
239         SGEN_SEMAPHORE_INIT (&suspend_ack_semaphore, 0);
240
241         sigfillset (&sinfo.sa_mask);
242         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
243         sinfo.sa_sigaction = suspend_handler;
244         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
245                 g_error ("failed sigaction");
246         }
247
248         sinfo.sa_handler = (void (*)(int))restart_handler;
249         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
250                 g_error ("failed sigaction");
251         }
252
253         sigfillset (&suspend_signal_mask);
254         sigdelset (&suspend_signal_mask, restart_signal_num);
255
256         sigemptyset (&suspend_ack_signal_mask);
257         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
258         
259 }
260
261 int
262 mono_gc_get_suspend_signal (void)
263 {
264         return suspend_signal_num;
265 }
266
267 int
268 mono_gc_get_restart_signal (void)
269 {
270         return restart_signal_num;
271 }
272 #endif
273 #endif