Replace all uses of DEBUG with SGEN_LOG and SGEN_ASSERT.
[mono.git] / mono / metadata / sgen-os-posix.c
1 /*
2  * sgen-os-posix.c: Simple generational GC.
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  *
11  * Permission is hereby granted, free of charge, to any person obtaining
12  * a copy of this software and associated documentation files (the
13  * "Software"), to deal in the Software without restriction, including
14  * without limitation the rights to use, copy, modify, merge, publish,
15  * distribute, sublicense, and/or sell copies of the Software, and to
16  * permit persons to whom the Software is furnished to do so, subject to
17  * the following conditions:
18  * 
19  * The above copyright notice and this permission notice shall be
20  * included in all copies or substantial portions of the Software.
21  * 
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29  */
30
31 #include "config.h"
32
33 #ifdef HAVE_SGEN_GC
34 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
35
36 #include <errno.h>
37 #include <glib.h>
38 #include "metadata/sgen-gc.h"
39 #include "metadata/gc-internal.h"
40 #include "metadata/sgen-archdep.h"
41 #include "metadata/object-internals.h"
42
43 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__)
44 const static int suspend_signal_num = SIGXFSZ;
45 #else
46 const static int suspend_signal_num = SIGPWR;
47 #endif
48 const static int restart_signal_num = SIGXCPU;
49
50 static MonoSemType suspend_ack_semaphore;
51 static MonoSemType *suspend_ack_semaphore_ptr;
52
53 static sigset_t suspend_signal_mask;
54 static sigset_t suspend_ack_signal_mask;
55
56 static void
57 suspend_thread (SgenThreadInfo *info, void *context)
58 {
59         int stop_count;
60 #ifndef USE_MONO_CTX
61         gpointer regs [ARCH_NUM_REGS];
62 #endif
63         gpointer stack_start;
64
65         g_assert (info->doing_handshake);
66
67         info->stopped_domain = mono_domain_get ();
68         info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
69         stop_count = sgen_global_stop_count;
70         /* duplicate signal */
71         if (0 && info->stop_count == stop_count)
72                 return;
73
74         sgen_fill_thread_info_for_suspend (info);
75
76         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
77         /* If stack_start is not within the limits, then don't set it
78            in info and we will be restarted. */
79         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
80                 info->stack_start = stack_start;
81
82 #ifdef USE_MONO_CTX
83                 if (context) {
84                         mono_sigctx_to_monoctx (context, &info->ctx);
85                 } else {
86                         memset (&info->ctx, 0, sizeof (MonoContext));
87                 }
88 #else
89                 if (context) {
90                         ARCH_COPY_SIGCTX_REGS (regs, context);
91                         memcpy (&info->regs, regs, sizeof (info->regs));
92                 } else {
93                         memset (&info->regs, 0, sizeof (info->regs));
94                 }
95 #endif
96         } else {
97                 g_assert (!info->stack_start);
98         }
99
100         /* Notify the JIT */
101         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
102                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context, NULL);
103
104         SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer)mono_native_thread_id_get ());
105
106         /*
107         Block the restart signal. 
108         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
109         which might miss the signal and get stuck.
110         */
111         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
112
113         /* notify the waiting thread */
114         MONO_SEM_POST (suspend_ack_semaphore_ptr);
115         info->stop_count = stop_count;
116
117         /* wait until we receive the restart signal */
118         do {
119                 info->signal = 0;
120                 sigsuspend (&suspend_signal_mask);
121         } while (info->signal != restart_signal_num && info->doing_handshake);
122
123         /* Unblock the restart signal. */
124         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
125
126         SGEN_LOG (4, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ());
127         /* notify the waiting thread */
128         MONO_SEM_POST (suspend_ack_semaphore_ptr);
129 }
130
131 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
132 static void
133 suspend_handler (int sig, siginfo_t *siginfo, void *context)
134 {
135         SgenThreadInfo *info;
136         int old_errno = errno;
137
138         info = mono_thread_info_current ();
139
140         if (info) {
141                 suspend_thread (info, context);
142         } else {
143                 /* This can happen while a thread is dying */
144                 //g_print ("no thread info in suspend\n");
145         }
146
147         errno = old_errno;
148 }
149
150 static void
151 restart_handler (int sig)
152 {
153         SgenThreadInfo *info;
154         int old_errno = errno;
155
156         info = mono_thread_info_current ();
157         /*
158         If the thread info is null is means we're currently in the process of cleaning up,
159         the pthread destructor has already kicked in and it has explicitly invoked the suspend handler.
160         
161         This means this thread has been suspended, TLS is dead, so the only option we have is to
162         rely on pthread_self () and seatch over the thread list.
163         */
164         if (!info)
165                 info = (SgenThreadInfo*)mono_thread_info_lookup (pthread_self ());
166
167         /*
168          * If a thread is dying there might be no thread info.  In
169          * that case we rely on info->doing_handshake.
170          */
171         if (info) {
172                 info->signal = restart_signal_num;
173                 SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer)mono_native_thread_id_get ());
174         }
175         errno = old_errno;
176 }
177
178 gboolean
179 sgen_resume_thread (SgenThreadInfo *info)
180 {
181         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
182 }
183
184 gboolean
185 sgen_suspend_thread (SgenThreadInfo *info)
186 {
187         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
188 }
189
190 void
191 sgen_wait_for_suspend_ack (int count)
192 {
193         int i, result;
194
195         for (i = 0; i < count; ++i) {
196                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
197                         if (errno != EINTR) {
198                                 g_error ("MONO_SEM_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
199                         }
200                 }
201         }
202 }
203
204 gboolean
205 sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
206 {
207     if (!p->doing_handshake)
208             return FALSE;
209
210     suspend_thread (p, NULL);
211     return TRUE;
212 }
213
214 int
215 sgen_thread_handshake (BOOL suspend)
216 {
217         int count, result;
218         SgenThreadInfo *info;
219         int signum = suspend ? suspend_signal_num : restart_signal_num;
220
221         MonoNativeThreadId me = mono_native_thread_id_get ();
222
223         count = 0;
224         FOREACH_THREAD_SAFE (info) {
225                 if (info->joined_stw == suspend)
226                         continue;
227                 info->joined_stw = suspend;
228                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
229                         continue;
230                 }
231                 if (info->gc_disabled)
232                         continue;
233                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
234                         continue;*/
235                 if (suspend) {
236                         g_assert (!info->doing_handshake);
237                         info->doing_handshake = TRUE;
238                 } else {
239                         g_assert (info->doing_handshake);
240                         info->doing_handshake = FALSE;
241                 }
242                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
243                 if (result == 0) {
244                         count++;
245                 } else {
246                         info->skip = 1;
247                 }
248         } END_FOREACH_THREAD_SAFE
249
250         sgen_wait_for_suspend_ack (count);
251
252         return count;
253 }
254
255 void
256 sgen_os_init (void)
257 {
258         struct sigaction sinfo;
259
260         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
261         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
262
263         sigfillset (&sinfo.sa_mask);
264         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
265         sinfo.sa_sigaction = suspend_handler;
266         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
267                 g_error ("failed sigaction");
268         }
269
270         sinfo.sa_handler = restart_handler;
271         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
272                 g_error ("failed sigaction");
273         }
274
275         sigfillset (&suspend_signal_mask);
276         sigdelset (&suspend_signal_mask, restart_signal_num);
277
278         sigemptyset (&suspend_ack_signal_mask);
279         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
280         
281 }
282
283 int
284 mono_gc_get_suspend_signal (void)
285 {
286         return suspend_signal_num;
287 }
288 #endif
289 #endif