49b7deff50cae62a57876855930d2e8e05cde1ba
[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 (signum == suspend_signal_num && info->stop_count == global_stop_count)
212                         continue;*/
213                 if (suspend) {
214                         g_assert (!info->doing_handshake);
215                         info->doing_handshake = TRUE;
216                 } else {
217                         g_assert (info->doing_handshake);
218                         info->doing_handshake = FALSE;
219                 }
220                 result = pthread_kill (mono_thread_info_get_tid (info), signum);
221                 if (result == 0) {
222                         count++;
223                 } else {
224                         info->skip = 1;
225                 }
226         } END_FOREACH_THREAD_SAFE
227
228         mono_sgen_wait_for_suspend_ack (count);
229
230         return count;
231 }
232
233 void
234 mono_sgen_os_init (void)
235 {
236         struct sigaction sinfo;
237
238         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
239         MONO_SEM_INIT (&suspend_ack_semaphore, 0);
240
241         sigfillset (&sinfo.sa_mask);
242         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
243         sinfo.sa_sigaction = suspend_handler;
244         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
245                 g_error ("failed sigaction");
246         }
247
248         sinfo.sa_handler = restart_handler;
249         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
250                 g_error ("failed sigaction");
251         }
252
253         sigfillset (&suspend_signal_mask);
254         sigdelset (&suspend_signal_mask, restart_signal_num);
255 }
256
257 int
258 mono_gc_get_suspend_signal (void)
259 {
260         return suspend_signal_num;
261 }
262 #endif
263 #endif