2 * mono-threads-posix-signals.c: Shared facility for Posix signals support
5 * Ludovic Henry (ludovic@gmail.com)
7 * (C) 2015 Xamarin, Inc
13 #include "mono-threads.h"
15 #if defined(USE_POSIX_BACKEND)
20 #include "mono-semaphore.h"
21 #include "mono-threads-posix-signals.h"
23 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
24 #define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
26 #define DEFAULT_SUSPEND_SIGNAL SIGPWR
28 #define DEFAULT_RESTART_SIGNAL SIGXCPU
30 static int suspend_signal_num;
31 static int restart_signal_num;
32 static int abort_signal_num;
34 static sigset_t suspend_signal_mask;
35 static sigset_t suspend_ack_signal_mask;
37 //Can't avoid the circular dep on this. Will be gone pretty soon
38 extern int mono_gc_get_suspend_signal (void);
41 signal_search_alternative (int min_signal)
43 #if !defined (SIGRTMIN)
44 g_error ("signal search only works with RTMIN");
47 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
48 for (i = MAX (min_signal, SIGRTMIN) + 1; i < SIGRTMAX; ++i) {
49 struct sigaction sinfo;
50 sigaction (i, NULL, &sinfo);
51 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
55 g_error ("Could not find an available signal");
60 signal_add_handler (int signo, gpointer handler, int flags)
62 #if !defined(__native_client__)
63 /*FIXME, move the code from mini to utils and do the right thing!*/
65 struct sigaction previous_sa;
68 sa.sa_sigaction = handler;
69 sigfillset (&sa.sa_mask);
71 sa.sa_flags = SA_SIGINFO | flags;
72 ret = sigaction (signo, &sa, &previous_sa);
79 suspend_signal_get (void)
81 #if defined(PLATFORM_ANDROID)
83 #elif !defined (SIGRTMIN)
90 static int suspend_signum = -1;
91 if (suspend_signum == -1)
92 suspend_signum = signal_search_alternative (-1);
93 return suspend_signum;
98 restart_signal_get (void)
100 #if defined(PLATFORM_ANDROID)
102 #elif !defined (SIGRTMIN)
109 static int resume_signum = -1;
110 if (resume_signum == -1)
111 resume_signum = signal_search_alternative (suspend_signal_get () + 1);
112 return resume_signum;
113 #endif /* SIGRTMIN */
118 abort_signal_get (void)
120 #if defined(PLATFORM_ANDROID)
122 #elif !defined (SIGRTMIN)
127 #endif /* SIGRTMIN */
129 static int abort_signum = -1;
130 if (abort_signum == -1)
131 abort_signum = signal_search_alternative (restart_signal_get () + 1);
133 #endif /* SIGRTMIN */
137 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
139 #if defined(__native_client__)
140 g_assert_not_reached ();
142 MonoThreadInfo *info;
143 int old_errno = errno;
145 info = mono_thread_info_current ();
146 info->signal = restart_signal_num;
152 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
154 #if defined(__native_client__)
155 g_assert_not_reached ();
157 int old_errno = errno;
158 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
161 MonoThreadInfo *current = mono_thread_info_current ();
164 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
165 if (current->syscall_break_signal) {
166 current->syscall_break_signal = FALSE;
167 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
168 mono_threads_notify_initiator_of_abort (current);
172 /* Have we raced with self suspend? */
173 if (!mono_threads_transition_finish_async_suspend (current)) {
174 current->suspend_can_continue = TRUE;
175 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
179 ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
181 /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
182 current->suspend_can_continue = ret;
185 Block the restart signal.
186 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
187 which might miss the signal and get stuck.
189 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
191 /* This thread is doomed, all we can do is give up and let the suspender recover. */
193 THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
194 mono_threads_transition_async_suspend_compensation (current);
196 /* We're done suspending */
197 mono_threads_notify_initiator_of_suspend (current);
199 /* Unblock the restart signal. */
200 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
205 /* We're done suspending */
206 mono_threads_notify_initiator_of_suspend (current);
210 sigsuspend (&suspend_signal_mask);
211 } while (current->signal != restart_signal_num);
213 /* Unblock the restart signal. */
214 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
216 if (current->async_target) {
217 #if MONO_ARCH_HAS_MONO_CONTEXT
218 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
219 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
220 current->async_target = current->user_data = NULL;
221 mono_monoctx_to_sigctx (&tmp, context);
223 g_error ("The new interruption machinery requires a working mono-context");
227 /* We're done resuming */
228 mono_threads_notify_initiator_of_resume (current);
231 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
237 abort_signal_handler (int _dummy, siginfo_t *info, void *context)
239 #if defined(__native_client__)
240 g_assert_not_reached ();
242 suspend_signal_handler (_dummy, info, context);
247 mono_threads_posix_init_signals (MonoThreadPosixInitSignals signals)
251 g_assert ((signals == MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART) ^ (signals == MONO_THREADS_POSIX_INIT_SIGNALS_ABORT));
253 sigemptyset (&signal_set);
256 case MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART: {
257 if (mono_thread_info_unified_management_enabled ()) {
258 suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
259 restart_signal_num = DEFAULT_RESTART_SIGNAL;
261 suspend_signal_num = suspend_signal_get ();
262 restart_signal_num = restart_signal_get ();
265 sigfillset (&suspend_signal_mask);
266 sigdelset (&suspend_signal_mask, restart_signal_num);
267 if (!mono_thread_info_unified_management_enabled ())
268 sigdelset (&suspend_signal_mask, mono_gc_get_suspend_signal ());
270 sigemptyset (&suspend_ack_signal_mask);
271 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
273 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
274 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
276 sigaddset (&signal_set, suspend_signal_num);
277 sigaddset (&signal_set, restart_signal_num);
281 case MONO_THREADS_POSIX_INIT_SIGNALS_ABORT: {
282 abort_signal_num = abort_signal_get ();
284 signal_add_handler (abort_signal_num, abort_signal_handler, 0);
286 sigaddset (&signal_set, abort_signal_num);
290 default: g_assert_not_reached ();
293 /* ensure all the new signals are unblocked */
294 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
298 mono_threads_posix_get_suspend_signal (void)
300 return suspend_signal_num;
304 mono_threads_posix_get_restart_signal (void)
306 return restart_signal_num;
310 mono_threads_posix_get_abort_signal (void)
312 return abort_signal_num;
315 #endif /* defined(USE_POSIX_BACKEND) */