[threads] Fix build
[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-semaphore.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 static int
40 signal_search_alternative (void)
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 = 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 = 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 abort_signal_get (void)
79 {
80 #if defined(PLATFORM_ANDROID)
81         return SIGTTIN;
82 #elif defined (SIGRTMIN)
83         static int abort_signum = -1;
84         if (abort_signum == -1)
85                 abort_signum = signal_search_alternative ();
86         return abort_signum;
87 #elif defined (SIGTTIN)
88         return SIGTTIN;
89 #else
90         return -1;
91 #endif
92 }
93
94 static void
95 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
96 {
97 #if defined(__native_client__)
98         g_assert_not_reached ();
99 #else
100         MonoThreadInfo *info;
101         int old_errno = errno;
102
103         info = mono_thread_info_current ();
104         info->signal = restart_signal_num;
105         errno = old_errno;
106 #endif
107 }
108
109 static void
110 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
111 {
112 #if defined(__native_client__)
113         g_assert_not_reached ();
114 #else
115         int old_errno = errno;
116         int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
117
118
119         MonoThreadInfo *current = mono_thread_info_current ();
120         gboolean ret;
121
122         THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
123         if (current->syscall_break_signal) {
124                 current->syscall_break_signal = FALSE;
125                 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
126                 mono_threads_notify_initiator_of_abort (current);
127                 goto done;
128         }
129
130         /* Have we raced with self suspend? */
131         if (!mono_threads_transition_finish_async_suspend (current)) {
132                 current->suspend_can_continue = TRUE;
133                 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
134                 goto done;
135         }
136
137         ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
138
139         /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
140         current->suspend_can_continue = ret;
141
142         /*
143         Block the restart signal.
144         We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
145         which might miss the signal and get stuck.
146         */
147         pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
148
149         /* This thread is doomed, all we can do is give up and let the suspender recover. */
150         if (!ret) {
151                 THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
152                 mono_threads_transition_async_suspend_compensation (current);
153
154                 /* We're done suspending */
155                 mono_threads_notify_initiator_of_suspend (current);
156
157                 /* Unblock the restart signal. */
158                 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
159
160                 goto done;
161         }
162
163         /* We're done suspending */
164         mono_threads_notify_initiator_of_suspend (current);
165
166         do {
167                 current->signal = 0;
168                 sigsuspend (&suspend_signal_mask);
169         } while (current->signal != restart_signal_num);
170
171         /* Unblock the restart signal. */
172         pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
173
174         if (current->async_target) {
175 #if MONO_ARCH_HAS_MONO_CONTEXT
176                 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
177                 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
178                 current->async_target = current->user_data = NULL;
179                 mono_monoctx_to_sigctx (&tmp, context);
180 #else
181                 g_error ("The new interruption machinery requires a working mono-context");
182 #endif
183         }
184
185         /* We're done resuming */
186         mono_threads_notify_initiator_of_resume (current);
187
188 done:
189         mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
190         errno = old_errno;
191 #endif
192 }
193
194 static void
195 abort_signal_handler (int _dummy, siginfo_t *info, void *context)
196 {
197 #if defined(__native_client__)
198         g_assert_not_reached ();
199 #else
200         suspend_signal_handler (_dummy, info, context);
201 #endif
202 }
203
204 void
205 mono_threads_posix_init_signals (MonoThreadPosixInitSignals signals)
206 {
207         sigset_t signal_set;
208
209         g_assert ((signals == MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART) ^ (signals == MONO_THREADS_POSIX_INIT_SIGNALS_ABORT));
210
211         sigemptyset (&signal_set);
212
213         switch (signals) {
214         case MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART: {
215                 suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
216                 restart_signal_num = DEFAULT_RESTART_SIGNAL;
217
218                 sigfillset (&suspend_signal_mask);
219                 sigdelset (&suspend_signal_mask, restart_signal_num);
220
221                 sigemptyset (&suspend_ack_signal_mask);
222                 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
223
224                 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
225                 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
226
227                 sigaddset (&signal_set, suspend_signal_num);
228                 sigaddset (&signal_set, restart_signal_num);
229
230                 break;
231         }
232         case MONO_THREADS_POSIX_INIT_SIGNALS_ABORT: {
233                 abort_signal_num = abort_signal_get ();
234
235                 signal_add_handler (abort_signal_num, abort_signal_handler, 0);
236
237                 sigaddset (&signal_set, abort_signal_num);
238
239                 break;
240         }
241         default: g_assert_not_reached ();
242         }
243
244         /* ensure all the new signals are unblocked */
245         sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
246 }
247
248 gint
249 mono_threads_posix_get_suspend_signal (void)
250 {
251         return suspend_signal_num;
252 }
253
254 gint
255 mono_threads_posix_get_restart_signal (void)
256 {
257         return restart_signal_num;
258 }
259
260 gint
261 mono_threads_posix_get_abort_signal (void)
262 {
263         return abort_signal_num;
264 }
265
266 #else /* defined(USE_POSIX_BACKEND) */
267
268 gint
269 mono_threads_posix_get_suspend_signal (void)
270 {
271         return -1;
272 }
273
274 gint
275 mono_threads_posix_get_restart_signal (void)
276 {
277         return -1;
278 }
279
280 gint
281 mono_threads_posix_get_abort_signal (void)
282 {
283         return -1;
284 }
285
286 #endif /* defined(USE_POSIX_BACKEND) */