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