Merge pull request #241 from viniciusjarina/fix2843
[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
55 static void
56 suspend_thread (SgenThreadInfo *info, void *context)
57 {
58         int stop_count;
59 #ifdef USE_MONO_CTX
60         MonoContext monoctx;
61 #else
62         gpointer regs [ARCH_NUM_REGS];
63 #endif
64         gpointer stack_start;
65
66         g_assert (info->doing_handshake);
67
68         info->stopped_domain = mono_domain_get ();
69         info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
70         stop_count = mono_sgen_global_stop_count;
71         /* duplicate signal */
72         if (0 && info->stop_count == stop_count)
73                 return;
74
75         mono_sgen_fill_thread_info_for_suspend (info);
76
77         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
78         /* If stack_start is not within the limits, then don't set it
79            in info and we will be restarted. */
80         if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
81                 info->stack_start = stack_start;
82
83 #ifdef USE_MONO_CTX
84                 if (context) {
85                         mono_sigctx_to_monoctx (context, &monoctx);
86                         info->monoctx = &monoctx;
87                 } else {
88                         info->monoctx = NULL;
89                 }
90 #else
91                 if (context) {
92                         ARCH_COPY_SIGCTX_REGS (regs, context);
93                         info->stopped_regs = regs;
94                 } else {
95                         info->stopped_regs = NULL;
96                 }
97 #endif
98         } else {
99                 g_assert (!info->stack_start);
100         }
101
102         /* Notify the JIT */
103         if (mono_gc_get_gc_callbacks ()->thread_suspend_func)
104                 mono_gc_get_gc_callbacks ()->thread_suspend_func (info->runtime_data, context);
105
106         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for suspend from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
107         /* notify the waiting thread */
108         MONO_SEM_POST (suspend_ack_semaphore_ptr);
109         info->stop_count = stop_count;
110
111         /* wait until we receive the restart signal */
112         do {
113                 info->signal = 0;
114                 sigsuspend (&suspend_signal_mask);
115         } while (info->signal != restart_signal_num && info->doing_handshake);
116
117         DEBUG (4, fprintf (gc_debug_file, "Posting suspend_ack_semaphore for resume from %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
118         /* notify the waiting thread */
119         MONO_SEM_POST (suspend_ack_semaphore_ptr);
120 }
121
122 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
123 static void
124 suspend_handler (int sig, siginfo_t *siginfo, void *context)
125 {
126         SgenThreadInfo *info;
127         int old_errno = errno;
128
129         info = mono_thread_info_current ();
130
131         if (info) {
132                 suspend_thread (info, context);
133         } else {
134                 /* This can happen while a thread is dying */
135                 //g_print ("no thread info in suspend\n");
136         }
137
138         errno = old_errno;
139 }
140
141 static void
142 restart_handler (int sig)
143 {
144         SgenThreadInfo *info;
145         int old_errno = errno;
146
147         info = mono_thread_info_current ();
148
149         /*
150          * If a thread is dying there might be no thread info.  In
151          * that case we rely on info->doing_handshake.
152          */
153         if (info) {
154                 info->signal = restart_signal_num;
155                 DEBUG (4, fprintf (gc_debug_file, "Restart handler in %p %p\n", info, (gpointer)mono_native_thread_id_get ()));
156         }
157
158         errno = old_errno;
159 }
160
161 gboolean
162 mono_sgen_resume_thread (SgenThreadInfo *info)
163 {
164         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
165 }
166
167 gboolean
168 mono_sgen_suspend_thread (SgenThreadInfo *info)
169 {
170         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
171 }
172
173 void
174 mono_sgen_wait_for_suspend_ack (int count)
175 {
176         int i, result;
177
178         for (i = 0; i < count; ++i) {
179                 while ((result = MONO_SEM_WAIT (suspend_ack_semaphore_ptr)) != 0) {
180                         if (errno != EINTR) {
181                                 g_error ("sem_wait ()");
182                         }
183                 }
184         }
185 }
186
187 gboolean
188 mono_sgen_park_current_thread_if_doing_handshake (SgenThreadInfo *p)
189 {
190     if (!p->doing_handshake)
191             return FALSE;
192
193     suspend_thread (p, NULL);
194     return TRUE;
195 }
196
197 int
198 mono_sgen_thread_handshake (BOOL suspend)
199 {
200         int count, result;
201         SgenThreadInfo *info;
202         int signum = suspend ? suspend_signal_num : restart_signal_num;
203
204         MonoNativeThreadId me = mono_native_thread_id_get ();
205
206         count = 0;
207         FOREACH_THREAD_SAFE (info) {
208                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
209                         continue;
210                 }
211                 if (info->gc_disabled)
212                         continue;
213                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
214                         continue;*/
215                 if (suspend) {
216                         g_assert (!info->doing_handshake);
217                         info->doing_handshake = TRUE;
218                 } else {
219                         g_assert (info->doing_handshake);
220                         info->doing_handshake = FALSE;
221                 }
222                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
223                 if (result == 0) {
224                         count++;
225                 } else {
226                         info->skip = 1;
227                 }
228         } END_FOREACH_THREAD_SAFE
229
230         mono_sgen_wait_for_suspend_ack (count);
231
232         return count;
233 }
234
235 void
236 mono_sgen_os_init (void)
237 {
238         struct sigaction sinfo;
239
240         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
241         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
242
243         sigfillset (&sinfo.sa_mask);
244         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
245         sinfo.sa_sigaction = suspend_handler;
246         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
247                 g_error ("failed sigaction");
248         }
249
250         sinfo.sa_handler = restart_handler;
251         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
252                 g_error ("failed sigaction");
253         }
254
255         sigfillset (&suspend_signal_mask);
256         sigdelset (&suspend_signal_mask, restart_signal_num);
257 }
258
259 int
260 mono_gc_get_suspend_signal (void)
261 {
262         return suspend_signal_num;
263 }
264 #endif
265 #endif