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