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