Merge pull request #4570 from lateralusX/jlorenss/visual_studio_msbuild_fix
[mono.git] / mono / utils / mono-threads-posix-signals.c
1 /**
2  * \file
3  * Shared facility for Posix signals support
4  *
5  * Author:
6  *      Ludovic Henry (ludovic@gmail.com)
7  *
8  * (C) 2015 Xamarin, Inc
9  */
10
11 #include <config.h>
12 #include <glib.h>
13
14 #include "mono-threads.h"
15
16 #if defined(USE_POSIX_BACKEND)
17
18 #include <errno.h>
19 #include <signal.h>
20
21 #include "mono-threads-debug.h"
22
23 gint
24 mono_threads_suspend_search_alternative_signal (void)
25 {
26 #if !defined (SIGRTMIN)
27         g_error ("signal search only works with RTMIN");
28 #else
29         int i;
30         /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
31         for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
32                 struct sigaction sinfo;
33                 sigaction (i, NULL, &sinfo);
34                 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
35                         return i;
36                 }
37         }
38         g_error ("Could not find an available signal");
39 #endif
40 }
41
42 #ifndef __native_client__
43
44 static int suspend_signal_num = -1;
45 static int restart_signal_num = -1;
46 static int abort_signal_num = -1;
47
48 static sigset_t suspend_signal_mask;
49 static sigset_t suspend_ack_signal_mask;
50
51 static void
52 signal_add_handler (int signo, void (*handler)(int, siginfo_t *, void *), int flags)
53 {
54         struct sigaction sa;
55         int ret;
56
57         sa.sa_sigaction = handler;
58         sigfillset (&sa.sa_mask);
59         sa.sa_flags = SA_SIGINFO | flags;
60         ret = sigaction (signo, &sa, NULL);
61         g_assert (ret != -1);
62 }
63
64 static int
65 abort_signal_get (void)
66 {
67 #if defined(PLATFORM_ANDROID)
68         return SIGTTIN;
69 #elif defined (SIGRTMIN)
70         static int abort_signum = -1;
71         if (abort_signum == -1)
72                 abort_signum = mono_threads_suspend_search_alternative_signal ();
73         return abort_signum;
74 #elif defined (SIGTTIN)
75         return SIGTTIN;
76 #else
77         g_error ("unable to get abort signal");
78 #endif
79 }
80
81 static int
82 suspend_signal_get (void)
83 {
84 #if defined(PLATFORM_ANDROID)
85         return SIGPWR;
86 #elif defined (SIGRTMIN)
87         static int suspend_signum = -1;
88         if (suspend_signum == -1)
89                 suspend_signum = mono_threads_suspend_search_alternative_signal ();
90         return suspend_signum;
91 #else
92 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
93         return SIGXFSZ;
94 #else
95         return SIGPWR;
96 #endif
97 #endif
98 }
99
100 static int
101 restart_signal_get (void)
102 {
103 #if defined(PLATFORM_ANDROID)
104         return SIGXCPU;
105 #elif defined (SIGRTMIN)
106         static int restart_signum = -1;
107         if (restart_signum == -1)
108                 restart_signum = mono_threads_suspend_search_alternative_signal ();
109         return restart_signum;
110 #else
111         return SIGXCPU;
112 #endif
113 }
114
115 static void
116 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
117 {
118         MonoThreadInfo *info;
119         int old_errno = errno;
120
121         info = mono_thread_info_current ();
122         info->signal = restart_signal_num;
123         errno = old_errno;
124 }
125
126 static void
127 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
128 {
129         int old_errno = errno;
130         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
131
132         MonoThreadInfo *current = mono_thread_info_current ();
133
134         THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
135         if (current->syscall_break_signal) {
136                 current->syscall_break_signal = FALSE;
137                 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
138                 mono_threads_notify_initiator_of_abort (current);
139                 goto done;
140         }
141
142         /* Have we raced with self suspend? */
143         if (!mono_threads_transition_finish_async_suspend (current)) {
144                 current->suspend_can_continue = TRUE;
145                 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
146                 goto done;
147         }
148
149         /*
150          * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
151          * as the thread might have been attached without the domain or lmf having been
152          * initialized yet.
153          *
154          * One way to fix that is to keep the thread suspended (wait for the restart
155          * signal), and make sgen aware that even if a thread might be suspended, there
156          * would be cases where you cannot scan its stack/registers. That would in fact
157          * consist in removing the async suspend compensation, and treat the case directly
158          * in sgen. That's also how it was done in the sgen specific suspend code.
159          */
160
161         /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
162         current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
163
164         if (!current->suspend_can_continue)
165                 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
166
167         /*
168         Block the restart signal.
169         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
170         which might miss the signal and get stuck.
171         */
172         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
173
174         /* We're done suspending */
175         mono_threads_notify_initiator_of_suspend (current);
176
177         do {
178                 current->signal = 0;
179                 sigsuspend (&suspend_signal_mask);
180         } while (current->signal != restart_signal_num);
181
182         /* Unblock the restart signal. */
183         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
184
185         if (current->async_target) {
186 #if MONO_ARCH_HAS_MONO_CONTEXT
187                 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
188                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
189                 current->user_data = NULL;
190                 current->async_target = NULL;
191                 mono_monoctx_to_sigctx (&tmp, context);
192 #else
193                 g_error ("The new interruption machinery requires a working mono-context");
194 #endif
195         }
196
197         /* We're done resuming */
198         mono_threads_notify_initiator_of_resume (current);
199
200 done:
201         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
202         errno = old_errno;
203 }
204
205 void
206 mono_threads_suspend_init_signals (void)
207 {
208         sigset_t signal_set;
209
210         sigemptyset (&signal_set);
211
212         /* add suspend signal */
213         suspend_signal_num = suspend_signal_get ();
214
215         signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
216
217         sigaddset (&signal_set, suspend_signal_num);
218
219         /* add restart signal */
220         restart_signal_num = restart_signal_get ();
221
222         sigfillset (&suspend_signal_mask);
223         sigdelset (&suspend_signal_mask, restart_signal_num);
224
225         sigemptyset (&suspend_ack_signal_mask);
226         sigaddset (&suspend_ack_signal_mask, restart_signal_num);
227
228         signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
229
230         sigaddset (&signal_set, restart_signal_num);
231
232         /* add abort signal */
233         abort_signal_num = abort_signal_get ();
234
235         /* the difference between abort and suspend here is made by not
236          * passing SA_RESTART, meaning we won't restart the syscall when
237          * receiving a signal */
238         signal_add_handler (abort_signal_num, suspend_signal_handler, 0);
239
240         sigaddset (&signal_set, abort_signal_num);
241
242         /* ensure all the new signals are unblocked */
243         sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
244
245         /*
246         On 32bits arm Android, signals with values >=32 are not usable as their headers ship a broken sigset_t.
247         See 5005c6f3fbc1da584c6a550281689cc23f59fe6d for more details.
248         */
249 #ifdef PLATFORM_ANDROID
250         g_assert (suspend_signal_num < 32);
251         g_assert (restart_signal_num < 32);
252         g_assert (abort_signal_num < 32);
253 #endif
254 }
255
256 gint
257 mono_threads_suspend_get_suspend_signal (void)
258 {
259         g_assert (suspend_signal_num != -1);
260         return suspend_signal_num;
261 }
262
263 gint
264 mono_threads_suspend_get_restart_signal (void)
265 {
266         g_assert (restart_signal_num != -1);
267         return restart_signal_num;
268 }
269
270 gint
271 mono_threads_suspend_get_abort_signal (void)
272 {
273         g_assert (abort_signal_num != -1);
274         return abort_signal_num;
275 }
276
277 #else
278
279 void
280 mono_threads_suspend_init_signals (void)
281 {
282         g_assert_not_reached ();
283 }
284
285 gint
286 mono_threads_suspend_get_suspend_signal (void)
287 {
288         return -1;
289 }
290
291 gint
292 mono_threads_suspend_get_restart_signal (void)
293 {
294         return -1;
295 }
296
297 gint
298 mono_threads_suspend_get_abort_signal (void)
299 {
300         return -1;
301 }
302
303 #endif /* __native_client__ */
304
305
306 #endif /* defined(USE_POSIX_BACKEND) */