Merge pull request #1066 from esdrubal/bug19313
[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 #ifdef HAVE_SGEN_GC
29 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
30
31 #include <errno.h>
32 #include <glib.h>
33 #include "metadata/sgen-gc.h"
34 #include "metadata/gc-internal.h"
35 #include "metadata/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 MonoSemType suspend_ack_semaphore;
47 static MonoSemType *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         gpointer stack_start;
60
61         info->stopped_domain = mono_domain_get ();
62         info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
63         info->signal = 0;
64         stop_count = sgen_global_stop_count;
65         /* duplicate signal */
66         if (0 && info->stop_count == stop_count)
67                 return;
68
69         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
70         /* If stack_start is not within the limits, then don't set it
71            in info and we will be restarted. */
72         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
73                 info->stack_start = stack_start;
74
75 #ifdef USE_MONO_CTX
76                 if (context) {
77                         mono_sigctx_to_monoctx (context, &info->ctx);
78                 } else {
79                         memset (&info->ctx, 0, sizeof (MonoContext));
80                 }
81 #else
82                 if (context) {
83                         ARCH_COPY_SIGCTX_REGS (regs, context);
84                         memcpy (&info->regs, regs, sizeof (info->regs));
85                 } else {
86                         memset (&info->regs, 0, sizeof (info->regs));
87                 }
88 #endif
89         } else {
90                 g_assert (!info->stack_start);
91         }
92
93         /* Notify the JIT */
94         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
95                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
96
97         SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
98
99         /*
100         Block the restart signal. 
101         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
102         which might miss the signal and get stuck.
103         */
104         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
105
106         /* notify the waiting thread */
107         MONO_SEM_POST (suspend_ack_semaphore_ptr);
108         info->stop_count = stop_count;
109
110         /* wait until we receive the restart signal */
111         do {
112                 info->signal = 0;
113                 sigsuspend (&suspend_signal_mask);
114         } while (info->signal != restart_signal_num);
115
116         /* Unblock the restart signal. */
117         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
118
119         SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
120         /* notify the waiting thread */
121         MONO_SEM_POST (suspend_ack_semaphore_ptr);
122 }
123
124 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
125 MONO_SIGNAL_HANDLER_FUNC (static, suspend_handler, (int sig, siginfo_t *siginfo, void *context))
126 {
127         /*
128          * The suspend signal handler potentially uses syscalls that
129          * can set errno, and it calls functions that use the hazard
130          * pointer machinery.  Since we're interrupting other code we
131          * must restore those to the values they had when we
132          * interrupted.
133          */
134
135         SgenThreadInfo *info;
136         int old_errno = errno;
137         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
138
139         info = mono_thread_info_current ();
140         suspend_thread (info, context);
141
142         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
143         errno = old_errno;
144 }
145
146 MONO_SIGNAL_HANDLER_FUNC (static, restart_handler, (int sig))
147 {
148         SgenThreadInfo *info;
149         int old_errno = errno;
150
151         info = mono_thread_info_current ();
152         info->signal = restart_signal_num;
153         SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
154         errno = old_errno;
155 }
156
157 gboolean
158 sgen_resume_thread (SgenThreadInfo *info)
159 {
160         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
161 }
162
163 gboolean
164 sgen_suspend_thread (SgenThreadInfo *info)
165 {
166         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
167 }
168
169 void
170 sgen_wait_for_suspend_ack (int count)
171 {
172         int i, result;
173
174         for (i = 0; i < count; ++i) {
175                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
176                         if (errno != EINTR) {
177                                 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
178                         }
179                 }
180         }
181 }
182
183 int
184 sgen_thread_handshake (BOOL suspend)
185 {
186         int count, result;
187         SgenThreadInfo *info;
188         int signum = suspend ? suspend_signal_num : restart_signal_num;
189
190         MonoNativeThreadId me = mono_native_thread_id_get ();
191
192         count = 0;
193         FOREACH_THREAD_SAFE (info) {
194                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
195                         continue;
196                 }
197                 if (info->gc_disabled)
198                         continue;
199                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
200                         continue;*/
201                 result = mono_threads_pthread_kill (info, signum);
202                 if (result == 0) {
203                         count++;
204                 } else {
205                         info->skip = 1;
206                 }
207         } END_FOREACH_THREAD_SAFE
208
209         sgen_wait_for_suspend_ack (count);
210
211         SGEN_LOG (4, "%s handshake for %d threads\n", suspend ? "suspend" : "resume", count);
212
213         return count;
214 }
215
216 void
217 sgen_os_init (void)
218 {
219         struct sigaction sinfo;
220
221         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
222         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
223
224         sigfillset (&sinfo.sa_mask);
225         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
226         sinfo.sa_sigaction = suspend_handler;
227         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
228                 g_error ("failed sigaction");
229         }
230
231         sinfo.sa_handler = (void*) restart_handler;
232         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
233                 g_error ("failed sigaction");
234         }
235
236         sigfillset (&suspend_signal_mask);
237         sigdelset (&suspend_signal_mask, restart_signal_num);
238
239         sigemptyset (&suspend_ack_signal_mask);
240         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
241         
242 }
243
244 int
245 mono_gc_get_suspend_signal (void)
246 {
247         return suspend_signal_num;
248 }
249
250 int
251 mono_gc_get_restart_signal (void)
252 {
253         return restart_signal_num;
254 }
255 #endif
256 #endif