Merge pull request #550 from knocte/master
[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         sgen_fill_thread_info_for_suspend (info);
70
71         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
72         /* If stack_start is not within the limits, then don't set it
73            in info and we will be restarted. */
74         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
75                 info->stack_start = stack_start;
76
77 #ifdef USE_MONO_CTX
78                 if (context) {
79                         mono_sigctx_to_monoctx (context, &info->ctx);
80                 } else {
81                         memset (&info->ctx, 0, sizeof (MonoContext));
82                 }
83 #else
84                 if (context) {
85                         ARCH_COPY_SIGCTX_REGS (regs, context);
86                         memcpy (&info->regs, regs, sizeof (info->regs));
87                 } else {
88                         memset (&info->regs, 0, sizeof (info->regs));
89                 }
90 #endif
91         } else {
92                 g_assert (!info->stack_start);
93         }
94
95         /* Notify the JIT */
96         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
97                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
98
99         SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
100
101         /*
102         Block the restart signal. 
103         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
104         which might miss the signal and get stuck.
105         */
106         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
107
108         /* notify the waiting thread */
109         MONO_SEM_POST (suspend_ack_semaphore_ptr);
110         info->stop_count = stop_count;
111
112         /* wait until we receive the restart signal */
113         do {
114                 info->signal = 0;
115                 sigsuspend (&suspend_signal_mask);
116         } while (info->signal != restart_signal_num && info->doing_handshake);
117
118         /* Unblock the restart signal. */
119         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
120
121         SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
122         /* notify the waiting thread */
123         MONO_SEM_POST (suspend_ack_semaphore_ptr);
124 }
125
126 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
127 static void
128 suspend_handler (int sig, siginfo_t *siginfo, void *context)
129 {
130         SgenThreadInfo *info;
131         int old_errno = errno;
132
133         info = mono_thread_info_current ();
134
135         if (info) {
136                 suspend_thread (info, context);
137         } else {
138                 /* This can happen while a thread is dying */
139                 //g_print ("no thread info in suspend\n");
140         }
141
142         errno = old_errno;
143 }
144
145 static void
146 restart_handler (int sig)
147 {
148         SgenThreadInfo *info;
149         int old_errno = errno;
150
151         info = mono_thread_info_current ();
152         /*
153         If the thread info is null is means we're currently in the process of cleaning up,
154         the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
155         
156         This means this thread has been suspended, TLS is dead, so the only option we have is to
157         rely on pthread_self () and seatch over the thread list.
158         */
159         if (!info)
160                 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
161
162         /*
163          * If a thread is dying there might be no thread info.  In
164          * that case we rely on info->doing_handshake.
165          */
166         if (info) {
167                 info->signal = restart_signal_num;
168                 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
169         }
170         errno = old_errno;
171 }
172
173 gboolean
174 sgen_resume_thread (SgenThreadInfo *info)
175 {
176         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
177 }
178
179 gboolean
180 sgen_suspend_thread (SgenThreadInfo *info)
181 {
182         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
183 }
184
185 void
186 sgen_wait_for_suspend_ack (int count)
187 {
188         int i, result;
189
190         for (i = 0; i < count; ++i) {
191                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
192                         if (errno != EINTR) {
193                                 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
194                         }
195                 }
196         }
197 }
198
199 gboolean
200 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
201 {
202     if (!p->doing_handshake)
203             return FALSE;
204
205     suspend_thread (p, NULL);
206     return TRUE;
207 }
208
209 int
210 sgen_thread_handshake (BOOL suspend)
211 {
212         int count, result;
213         SgenThreadInfo *info;
214         int signum = suspend ? suspend_signal_num : restart_signal_num;
215
216         MonoNativeThreadId me = mono_native_thread_id_get ();
217
218         count = 0;
219         FOREACH_THREAD_SAFE (info) {
220                 if (info->joined_stw == suspend)
221                         continue;
222                 info->joined_stw = suspend;
223                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
224                         continue;
225                 }
226                 if (info->gc_disabled)
227                         continue;
228                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
229                         continue;*/
230                 if (suspend) {
231                         g_assert (!info->doing_handshake);
232                         info->doing_handshake = TRUE;
233                 } else {
234                         g_assert (info->doing_handshake);
235                         info->doing_handshake = FALSE;
236                 }
237                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
238                 if (result == 0) {
239                         count++;
240                 } else {
241                         info->skip = 1;
242                 }
243         } END_FOREACH_THREAD_SAFE
244
245         sgen_wait_for_suspend_ack (count);
246
247         return count;
248 }
249
250 void
251 sgen_os_init (void)
252 {
253         struct sigaction sinfo;
254
255         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
256         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
257
258         sigfillset (&sinfo.sa_mask);
259         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
260         sinfo.sa_sigaction = suspend_handler;
261         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
262                 g_error ("failed sigaction");
263         }
264
265         sinfo.sa_handler = restart_handler;
266         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
267                 g_error ("failed sigaction");
268         }
269
270         sigfillset (&suspend_signal_mask);
271         sigdelset (&suspend_signal_mask, restart_signal_num);
272
273         sigemptyset (&suspend_ack_signal_mask);
274         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
275         
276 }
277
278 int
279 mono_gc_get_suspend_signal (void)
280 {
281         return suspend_signal_num;
282 }
283 #endif
284 #endif