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-threads-debug.h"
22 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
23 #define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
25 #define DEFAULT_SUSPEND_SIGNAL SIGPWR
27 #define DEFAULT_RESTART_SIGNAL SIGXCPU
29 static int abort_signal_num;
31 static sigset_t suspend_signal_mask;
32 static sigset_t suspend_ack_signal_mask;
35 mono_threads_suspend_search_alternative_signal (void)
37 #if !defined (SIGRTMIN)
38 g_error ("signal search only works with RTMIN");
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) {
49 g_error ("Could not find an available signal");
54 signal_add_handler (int signo, gpointer handler, int flags)
56 #if defined(__native_client__)
57 g_assert_not_reached ();
59 /*FIXME, move the code from mini to utils and do the right thing!*/
61 struct sigaction previous_sa;
64 sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
65 sigfillset (&sa.sa_mask);
67 sa.sa_flags = SA_SIGINFO | flags;
68 ret = sigaction (signo, &sa, &previous_sa);
75 abort_signal_get (void)
77 #if defined(PLATFORM_ANDROID)
79 #elif defined (SIGRTMIN)
80 static int abort_signum = -1;
81 if (abort_signum == -1)
82 abort_signum = mono_threads_suspend_search_alternative_signal ();
84 #elif defined (SIGTTIN)
92 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
94 #if defined(__native_client__)
95 g_assert_not_reached ();
98 int old_errno = errno;
100 info = mono_thread_info_current ();
101 info->signal = DEFAULT_RESTART_SIGNAL;
107 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
109 #if defined(__native_client__)
110 g_assert_not_reached ();
112 int old_errno = errno;
113 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
116 MonoThreadInfo *current = mono_thread_info_current ();
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);
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));
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
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.
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 (¤t->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
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));
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.
157 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
159 /* We're done suspending */
160 mono_threads_notify_initiator_of_suspend (current);
164 sigsuspend (&suspend_signal_mask);
165 } while (current->signal != DEFAULT_RESTART_SIGNAL);
167 /* Unblock the restart signal. */
168 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
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);
178 g_error ("The new interruption machinery requires a working mono-context");
182 /* We're done resuming */
183 mono_threads_notify_initiator_of_resume (current);
186 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
192 mono_threads_suspend_init_signals (void)
196 sigemptyset (&signal_set);
198 sigfillset (&suspend_signal_mask);
199 sigdelset (&suspend_signal_mask, DEFAULT_RESTART_SIGNAL);
201 sigemptyset (&suspend_ack_signal_mask);
202 sigaddset (&suspend_ack_signal_mask, DEFAULT_RESTART_SIGNAL);
204 signal_add_handler (DEFAULT_SUSPEND_SIGNAL, suspend_signal_handler, SA_RESTART);
205 signal_add_handler (DEFAULT_RESTART_SIGNAL, restart_signal_handler, SA_RESTART);
207 sigaddset (&signal_set, DEFAULT_SUSPEND_SIGNAL);
208 sigaddset (&signal_set, DEFAULT_RESTART_SIGNAL);
210 abort_signal_num = abort_signal_get ();
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);
217 sigaddset (&signal_set, abort_signal_num);
219 /* ensure all the new signals are unblocked */
220 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
224 mono_threads_suspend_get_suspend_signal (void)
226 return DEFAULT_SUSPEND_SIGNAL;
230 mono_threads_suspend_get_restart_signal (void)
232 return DEFAULT_RESTART_SIGNAL;
236 mono_threads_suspend_get_abort_signal (void)
238 return abort_signal_num;
241 #endif /* defined(USE_POSIX_BACKEND) */