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