Merge pull request #2102 from AdamBurgess/master
[mono.git] / mono / utils / mono-threads-posix-signals.c
1 /*
2  * mono-threads-posix-signals.c: Shared facility for Posix signals support
3  *
4  * Author:
5  *      Ludovic Henry (ludovic@gmail.com)
6  *
7  * (C) 2015 Xamarin, Inc
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include "mono-threads.h"
14
15 #if defined(USE_POSIX_BACKEND)
16
17 #include <errno.h>
18 #include <signal.h>
19
20 #include "mono-semaphore.h"
21 #include "mono-threads-posix-signals.h"
22
23 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
24 #define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
25 #else
26 #define DEFAULT_SUSPEND_SIGNAL SIGPWR
27 #endif
28 #define DEFAULT_RESTART_SIGNAL SIGXCPU
29
30 static int suspend_signal_num;
31 static int restart_signal_num;
32 static int abort_signal_num;
33
34 static sigset_t suspend_signal_mask;
35 static sigset_t suspend_ack_signal_mask;
36
37 //Can't avoid the circular dep on this. Will be gone pretty soon
38 extern int mono_gc_get_suspend_signal (void);
39
40 static int
41 signal_search_alternative (int min_signal)
42 {
43 #if !defined (SIGRTMIN)
44         g_error ("signal search only works with RTMIN");
45 #else
46         int i;
47         /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
48         for (i = MAX (min_signal, SIGRTMIN) + 1; i < SIGRTMAX; ++i) {
49                 struct sigaction sinfo;
50                 sigaction (i, NULL, &sinfo);
51                 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
52                         return i;
53                 }
54         }
55         g_error ("Could not find an available signal");
56 #endif
57 }
58
59 static void
60 signal_add_handler (int signo, gpointer handler, int flags)
61 {
62 #if !defined(__native_client__)
63         /*FIXME, move the code from mini to utils and do the right thing!*/
64         struct sigaction sa;
65         struct sigaction previous_sa;
66         int ret;
67
68         sa.sa_sigaction = handler;
69         sigfillset (&sa.sa_mask);
70
71         sa.sa_flags = SA_SIGINFO | flags;
72         ret = sigaction (signo, &sa, &previous_sa);
73
74         g_assert (ret != -1);
75 #endif
76 }
77
78 static int
79 suspend_signal_get (void)
80 {
81 #if defined(PLATFORM_ANDROID)
82         return SIGUNUSED;
83 #elif !defined (SIGRTMIN)
84 #ifdef SIGUSR1
85         return SIGUSR1;
86 #else
87         return -1;
88 #endif /* SIGUSR1 */
89 #else
90         static int suspend_signum = -1;
91         if (suspend_signum == -1)
92                 suspend_signum = signal_search_alternative (-1);
93         return suspend_signum;
94 #endif /* SIGRTMIN */
95 }
96
97 static int
98 restart_signal_get (void)
99 {
100 #if defined(PLATFORM_ANDROID)
101         return SIGTTOU;
102 #elif !defined (SIGRTMIN)
103 #ifdef SIGUSR2
104         return SIGUSR2;
105 #else
106         return -1;
107 #endif /* SIGUSR1 */
108 #else
109         static int resume_signum = -1;
110         if (resume_signum == -1)
111                 resume_signum = signal_search_alternative (suspend_signal_get () + 1);
112         return resume_signum;
113 #endif /* SIGRTMIN */
114 }
115
116
117 static int
118 abort_signal_get (void)
119 {
120 #if defined(PLATFORM_ANDROID)
121         return SIGTTIN;
122 #elif !defined (SIGRTMIN)
123 #ifdef SIGTTIN
124         return SIGTTIN;
125 #else
126         return -1;
127 #endif /* SIGRTMIN */
128 #else
129         static int abort_signum = -1;
130         if (abort_signum == -1)
131                 abort_signum = signal_search_alternative (restart_signal_get () + 1);
132         return abort_signum;
133 #endif /* SIGRTMIN */
134 }
135
136 static void
137 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
138 {
139 #if defined(__native_client__)
140         g_assert_not_reached ();
141 #else
142         MonoThreadInfo *info;
143         int old_errno = errno;
144
145         info = mono_thread_info_current ();
146         info->signal = restart_signal_num;
147         errno = old_errno;
148 #endif
149 }
150
151 static void
152 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
153 {
154 #if defined(__native_client__)
155         g_assert_not_reached ();
156 #else
157         int old_errno = errno;
158         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
159
160
161         MonoThreadInfo *current = mono_thread_info_current ();
162         gboolean ret;
163
164         THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
165         if (current->syscall_break_signal) {
166                 current->syscall_break_signal = FALSE;
167                 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
168                 mono_threads_notify_initiator_of_abort (current);
169                 goto done;
170         }
171
172         /* Have we raced with self suspend? */
173         if (!mono_threads_transition_finish_async_suspend (current)) {
174                 current->suspend_can_continue = TRUE;
175                 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
176                 goto done;
177         }
178
179         ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
180
181         /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
182         current->suspend_can_continue = ret;
183
184         /*
185         Block the restart signal.
186         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
187         which might miss the signal and get stuck.
188         */
189         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
190
191         /* This thread is doomed, all we can do is give up and let the suspender recover. */
192         if (!ret) {
193                 THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
194                 mono_threads_transition_async_suspend_compensation (current);
195
196                 /* We're done suspending */
197                 mono_threads_notify_initiator_of_suspend (current);
198
199                 /* Unblock the restart signal. */
200                 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
201
202                 goto done;
203         }
204
205         /* We're done suspending */
206         mono_threads_notify_initiator_of_suspend (current);
207
208         do {
209                 current->signal = 0;
210                 sigsuspend (&suspend_signal_mask);
211         } while (current->signal != restart_signal_num);
212
213         /* Unblock the restart signal. */
214         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
215
216         if (current->async_target) {
217 #if MONO_ARCH_HAS_MONO_CONTEXT
218                 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
219                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
220                 current->async_target = current->user_data = NULL;
221                 mono_monoctx_to_sigctx (&tmp, context);
222 #else
223                 g_error ("The new interruption machinery requires a working mono-context");
224 #endif
225         }
226
227         /* We're done resuming */
228         mono_threads_notify_initiator_of_resume (current);
229
230 done:
231         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
232         errno = old_errno;
233 #endif
234 }
235
236 static void
237 abort_signal_handler (int _dummy, siginfo_t *info, void *context)
238 {
239 #if defined(__native_client__)
240         g_assert_not_reached ();
241 #else
242         suspend_signal_handler (_dummy, info, context);
243 #endif
244 }
245
246 void
247 mono_threads_posix_init_signals (MonoThreadPosixInitSignals signals)
248 {
249         sigset_t signal_set;
250
251         g_assert ((signals == MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART) ^ (signals == MONO_THREADS_POSIX_INIT_SIGNALS_ABORT));
252
253         sigemptyset (&signal_set);
254
255         switch (signals) {
256         case MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART: {
257                 if (mono_thread_info_unified_management_enabled ()) {
258                         suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
259                         restart_signal_num = DEFAULT_RESTART_SIGNAL;
260                 } else {
261                         suspend_signal_num = suspend_signal_get ();
262                         restart_signal_num = restart_signal_get ();
263                 }
264
265                 sigfillset (&suspend_signal_mask);
266                 sigdelset (&suspend_signal_mask, restart_signal_num);
267                 if (!mono_thread_info_unified_management_enabled ())
268                         sigdelset (&suspend_signal_mask, mono_gc_get_suspend_signal ());
269
270                 sigemptyset (&suspend_ack_signal_mask);
271                 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
272
273                 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
274                 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
275
276                 sigaddset (&signal_set, suspend_signal_num);
277                 sigaddset (&signal_set, restart_signal_num);
278
279                 break;
280         }
281         case MONO_THREADS_POSIX_INIT_SIGNALS_ABORT: {
282                 abort_signal_num = abort_signal_get ();
283
284                 signal_add_handler (abort_signal_num, abort_signal_handler, 0);
285
286                 sigaddset (&signal_set, abort_signal_num);
287
288                 break;
289         }
290         default: g_assert_not_reached ();
291         }
292
293         /* ensure all the new signals are unblocked */
294         sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
295 }
296
297 gint
298 mono_threads_posix_get_suspend_signal (void)
299 {
300         return suspend_signal_num;
301 }
302
303 gint
304 mono_threads_posix_get_restart_signal (void)
305 {
306         return restart_signal_num;
307 }
308
309 gint
310 mono_threads_posix_get_abort_signal (void)
311 {
312         return abort_signal_num;
313 }
314
315 #endif /* defined(USE_POSIX_BACKEND) */