First set of licensing changes
[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  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13  */
14
15 #include "config.h"
16
17 #if defined(HAVE_SGEN_GC) && !defined(USE_COOP_GC)
18 #if !defined(__MACH__) && !MONO_MACH_ARCH_SUPPORTED && defined(HAVE_PTHREAD_KILL)
19
20 #include <errno.h>
21 #include <glib.h>
22 #include "sgen/sgen-gc.h"
23 #include "metadata/gc-internals.h"
24 #include "sgen/sgen-archdep.h"
25 #include "metadata/object-internals.h"
26 #include "utils/mono-signal-handler.h"
27
28 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
29 const static int suspend_signal_num = SIGXFSZ;
30 #else
31 const static int suspend_signal_num = SIGPWR;
32 #endif
33 const static int restart_signal_num = SIGXCPU;
34
35 static SgenSemaphore suspend_ack_semaphore;
36 static SgenSemaphore *suspend_ack_semaphore_ptr;
37
38 static sigset_t suspend_signal_mask;
39 static sigset_t suspend_ack_signal_mask;
40
41 static void
42 suspend_thread (SgenThreadInfo *info, void *context)
43 {
44         int stop_count;
45 #ifndef USE_MONO_CTX
46         gpointer regs [ARCH_NUM_REGS];
47 #endif
48         MonoContext ctx;
49         gpointer stack_start;
50
51         info->client_info.stopped_domain = mono_domain_get ();
52         info->client_info.signal = 0;
53         stop_count = sgen_global_stop_count;
54         /* duplicate signal */
55         if (0 && info->client_info.stop_count == stop_count)
56                 return;
57
58 #ifdef USE_MONO_CTX
59         if (context) {
60                 mono_sigctx_to_monoctx (context, &ctx);
61                 info->client_info.stopped_ip = MONO_CONTEXT_GET_IP (&ctx);
62                 stack_start = (((guint8 *) MONO_CONTEXT_GET_SP (&ctx)) - REDZONE_SIZE);
63         } else {
64                 info->client_info.stopped_ip = NULL;
65                 stack_start = NULL;
66         }
67 #else
68         info->client_info.stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
69         stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
70 #endif
71
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->client_info.stack_start_limit && stack_start <= info->client_info.stack_end) {
75                 info->client_info.stack_start = stack_start;
76
77 #ifdef USE_MONO_CTX
78                 if (context) {
79                         memcpy (&info->client_info.ctx, &ctx, sizeof (MonoContext));
80                 } else {
81                         memset (&info->client_info.ctx, 0, sizeof (MonoContext));
82                 }
83 #else
84                 if (context) {
85                         ARCH_COPY_SIGCTX_REGS (regs, context);
86                         memcpy (&info->client_info.regs, regs, sizeof (info->client_info.regs));
87                 } else {
88                         memset (&info->client_info.regs, 0, sizeof (info->client_info.regs));
89                 }
90 #endif
91         } else {
92                 g_assert (!info->client_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->client_info.runtime_data, context, NULL);
98
99         SGEN_LOG (4, "Posting suspend_ack_semaphore for suspend from %p %p", info, (gpointer) (gsize) 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         SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr);
110         info->client_info.stop_count = stop_count;
111
112         /* wait until we receive the restart signal */
113         do {
114                 info->client_info.signal = 0;
115                 sigsuspend (&suspend_signal_mask);
116         } while (info->client_info.signal != restart_signal_num);
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) (gsize) mono_native_thread_id_get ());
122         /* notify the waiting thread */
123         SGEN_SEMAPHORE_POST (suspend_ack_semaphore_ptr);
124 }
125
126 /* LOCKING: assumes the GC lock is held (by the stopping thread) */
127 MONO_SIG_HANDLER_FUNC (static, suspend_handler)
128 {
129         /*
130          * The suspend signal handler potentially uses syscalls that
131          * can set errno, and it calls functions that use the hazard
132          * pointer machinery.  Since we're interrupting other code we
133          * must restore those to the values they had when we
134          * interrupted.
135          */
136         SgenThreadInfo *info;
137         int old_errno = errno;
138         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
139         MONO_SIG_HANDLER_GET_CONTEXT;
140
141         info = mono_thread_info_current ();
142         suspend_thread (info, ctx);
143
144         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
145         errno = old_errno;
146 }
147
148 MONO_SIG_HANDLER_FUNC (static, restart_handler)
149 {
150         SgenThreadInfo *info;
151         int old_errno = errno;
152
153         info = mono_thread_info_current ();
154         info->client_info.signal = restart_signal_num;
155         SGEN_LOG (4, "Restart handler in %p %p", info, (gpointer) (gsize) mono_native_thread_id_get ());
156         errno = old_errno;
157 }
158
159 gboolean
160 sgen_resume_thread (SgenThreadInfo *info)
161 {
162         return mono_threads_pthread_kill (info, restart_signal_num) == 0;
163 }
164
165 gboolean
166 sgen_suspend_thread (SgenThreadInfo *info)
167 {
168         return mono_threads_pthread_kill (info, suspend_signal_num) == 0;
169 }
170
171 void
172 sgen_wait_for_suspend_ack (int count)
173 {
174         int i, result;
175
176         for (i = 0; i < count; ++i) {
177                 while ((result = SGEN_SEMAPHORE_WAIT (suspend_ack_semaphore_ptr)) != 0) {
178                         if (errno != EINTR) {
179                                 g_error ("SGEN_SEMAPHORE_WAIT FAILED with %d errno %d (%s)", result, errno, strerror (errno));
180                         }
181                 }
182         }
183 }
184
185 int
186 sgen_thread_handshake (BOOL suspend)
187 {
188         int count, result;
189         int signum = suspend ? suspend_signal_num : restart_signal_num;
190
191         MonoNativeThreadId me = mono_native_thread_id_get ();
192
193         count = 0;
194         mono_thread_info_current ()->client_info.suspend_done = TRUE;
195         FOREACH_THREAD (info) {
196                 if (mono_native_thread_id_equals (mono_thread_info_get_tid (info), me)) {
197                         continue;
198                 }
199                 info->client_info.suspend_done = FALSE;
200                 if (info->client_info.gc_disabled)
201                         continue;
202                 /*if (signum == suspend_signal_num && info->stop_count == global_stop_count)
203                         continue;*/
204                 result = mono_threads_pthread_kill (info, signum);
205                 if (result == 0) {
206                         count++;
207                 } else {
208                         info->client_info.skip = 1;
209                 }
210         } FOREACH_THREAD_END
211
212         sgen_wait_for_suspend_ack (count);
213
214         SGEN_LOG (4, "%s handshake for %d threads\n", suspend ? "suspend" : "resume", count);
215
216         return count;
217 }
218
219 void
220 sgen_os_init (void)
221 {
222         struct sigaction sinfo;
223
224         if (mono_thread_info_unified_management_enabled ())
225                 return;
226
227         suspend_ack_semaphore_ptr = &suspend_ack_semaphore;
228         SGEN_SEMAPHORE_INIT (&suspend_ack_semaphore, 0);
229
230         sigfillset (&sinfo.sa_mask);
231         sinfo.sa_flags = SA_RESTART | SA_SIGINFO;
232         sinfo.sa_sigaction = suspend_handler;
233         if (sigaction (suspend_signal_num, &sinfo, NULL) != 0) {
234                 g_error ("failed sigaction");
235         }
236
237         sinfo.sa_handler = (void (*)(int))restart_handler;
238         if (sigaction (restart_signal_num, &sinfo, NULL) != 0) {
239                 g_error ("failed sigaction");
240         }
241
242         sigfillset (&suspend_signal_mask);
243         sigdelset (&suspend_signal_mask, restart_signal_num);
244
245         sigemptyset (&suspend_ack_signal_mask);
246         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
247         
248 }
249
250 int
251 mono_gc_get_suspend_signal (void)
252 {
253         return suspend_signal_num;
254 }
255
256 int
257 mono_gc_get_restart_signal (void)
258 {
259         return restart_signal_num;
260 }
261 #endif
262 #endif