minor fix for bug 9520:
[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
38 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
39 const static int suspend_signal_num = SIGXFSZ;
40 #else
41 const static int suspend_signal_num = SIGPWR;
42 #endif
43 const static int restart_signal_num = SIGXCPU;
44
45 static MonoSemType suspend_ack_semaphore;
46 static MonoSemType *suspend_ack_semaphore_ptr;
47
48 static sigset_t suspend_signal_mask;
49 static sigset_t suspend_ack_signal_mask;
50
51 static void
52 suspend_thread (SgenThreadInfo *info, void *context)
53 {
54         int stop_count;
55 #ifndef USE_MONO_CTX
56         gpointer regs [ARCH_NUM_REGS];
57 #endif
58         gpointer stack_start;
59
60         g_assert (info->doing_handshake);
61
62         info->stopped_domain = mono_domain_get ();
63         info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
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 && info->doing_handshake);
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 static void
126 suspend_handler (int sig, siginfo_t *siginfo, void *context)
127 {
128         SgenThreadInfo *info;
129         int old_errno = errno;
130
131         info = mono_thread_info_current ();
132
133         if (info) {
134                 suspend_thread (info, context);
135         } else {
136                 /* This can happen while a thread is dying */
137                 //g_print ("no thread info in suspend\n");
138         }
139
140         errno = old_errno;
141 }
142
143 static void
144 restart_handler (int sig)
145 {
146         SgenThreadInfo *info;
147         int old_errno = errno;
148
149         info = mono_thread_info_current ();
150         /*
151         If the thread info is null is means we're currently in the process of cleaning up,
152         the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
153         
154         This means this thread has been suspended, TLS is dead, so the only option we have is to
155         rely on pthread_self () and seatch over the thread list.
156         */
157         if (!info)
158                 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
159
160         /*
161          * If a thread is dying there might be no thread info.  In
162          * that case we rely on info->doing_handshake.
163          */
164         if (info) {
165                 info->signal = restart_signal_num;
166                 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
167         }
168         errno = old_errno;
169 }
170
171 gboolean
172 sgen_resume_thread (SgenThreadInfo *info)
173 {
174         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
175 }
176
177 gboolean
178 sgen_suspend_thread (SgenThreadInfo *info)
179 {
180         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
181 }
182
183 void
184 sgen_wait_for_suspend_ack (int count)
185 {
186         int i, result;
187
188         for (i = 0; i < count; ++i) {
189                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
190                         if (errno != EINTR) {
191                                 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
192                         }
193                 }
194         }
195 }
196
197 gboolean
198 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
199 {
200     if (!p->doing_handshake)
201             return FALSE;
202
203     suspend_thread (p, NULL);
204     return TRUE;
205 }
206
207 int
208 sgen_thread_handshake (BOOL suspend)
209 {
210         int count, result;
211         SgenThreadInfo *info;
212         int signum = suspend ? suspend_signal_num : restart_signal_num;
213
214         MonoNativeThreadId me = mono_native_thread_id_get ();
215
216         count = 0;
217         FOREACH_THREAD_SAFE (info) {
218                 if (info->joined_stw == suspend)
219                         continue;
220                 info->joined_stw = suspend;
221                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
222                         continue;
223                 }
224                 if (info->gc_disabled)
225                         continue;
226                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
227                         continue;*/
228                 if (suspend) {
229                         g_assert (!info->doing_handshake);
230                         info->doing_handshake = TRUE;
231                 } else {
232                         g_assert (info->doing_handshake);
233                         info->doing_handshake = FALSE;
234                 }
235                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
236                 if (result == 0) {
237                         count++;
238                 } else {
239                         info->skip = 1;
240                 }
241         } END_FOREACH_THREAD_SAFE
242
243         sgen_wait_for_suspend_ack (count);
244
245         return count;
246 }
247
248 void
249 sgen_os_init (void)
250 {
251         struct sigaction sinfo;
252
253         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
254         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
255
256         sigfillset (&sinfo.sa_mask);
257         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
258         sinfo.sa_sigaction = suspend_handler;
259         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
260                 g_error ("failed sigaction");
261         }
262
263         sinfo.sa_handler = restart_handler;
264         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
265                 g_error ("failed sigaction");
266         }
267
268         sigfillset (&suspend_signal_mask);
269         sigdelset (&suspend_signal_mask, restart_signal_num);
270
271         sigemptyset (&suspend_ack_signal_mask);
272         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
273         
274 }
275
276 int
277 mono_gc_get_suspend_signal (void)
278 {
279         return suspend_signal_num;
280 }
281 #endif
282 #endif