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