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-posix-signals.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 suspend_signal_num;
30 static int restart_signal_num;
31 static int abort_signal_num;
33 static sigset_t suspend_signal_mask;
34 static sigset_t suspend_ack_signal_mask;
36 //Can't avoid the circular dep on this. Will be gone pretty soon
37 extern int mono_gc_get_suspend_signal (void);
40 mono_threads_posix_signal_search_alternative (int min_signal)
42 #if !defined (SIGRTMIN)
43 g_error ("signal search only works with RTMIN");
46 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
47 for (i = MAX (min_signal, 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) {
54 g_error ("Could not find an available signal");
59 signal_add_handler (int signo, gpointer handler, int flags)
61 #if !defined(__native_client__)
62 /*FIXME, move the code from mini to utils and do the right thing!*/
64 struct sigaction previous_sa;
67 sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
68 sigfillset (&sa.sa_mask);
70 sa.sa_flags = SA_SIGINFO | flags;
71 ret = sigaction (signo, &sa, &previous_sa);
78 suspend_signal_get (void)
80 #if defined(PLATFORM_ANDROID)
82 #elif !defined (SIGRTMIN)
89 static int suspend_signum = -1;
90 if (suspend_signum == -1)
91 suspend_signum = mono_threads_posix_signal_search_alternative (-1);
92 return suspend_signum;
97 restart_signal_get (void)
99 #if defined(PLATFORM_ANDROID)
101 #elif !defined (SIGRTMIN)
108 static int resume_signum = -1;
109 if (resume_signum == -1)
110 resume_signum = mono_threads_posix_signal_search_alternative (suspend_signal_get () + 1);
111 return resume_signum;
112 #endif /* SIGRTMIN */
117 abort_signal_get (void)
119 #if defined(PLATFORM_ANDROID)
121 #elif !defined (SIGRTMIN)
126 #endif /* SIGRTMIN */
128 static int abort_signum = -1;
129 if (abort_signum == -1)
130 abort_signum = mono_threads_posix_signal_search_alternative (restart_signal_get () + 1);
132 #endif /* SIGRTMIN */
136 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
138 #if defined(__native_client__)
139 g_assert_not_reached ();
141 MonoThreadInfo *info;
142 int old_errno = errno;
144 info = mono_thread_info_current ();
145 info->signal = restart_signal_num;
151 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
153 #if defined(__native_client__)
154 g_assert_not_reached ();
156 int old_errno = errno;
157 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
160 MonoThreadInfo *current = mono_thread_info_current ();
163 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
164 if (current->syscall_break_signal) {
165 current->syscall_break_signal = FALSE;
166 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
167 mono_threads_notify_initiator_of_abort (current);
171 /* Have we raced with self suspend? */
172 if (!mono_threads_transition_finish_async_suspend (current)) {
173 current->suspend_can_continue = TRUE;
174 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
179 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
180 * as the thread might have been attached without the domain or lmf having been
183 * One way to fix that is to keep the thread suspended (wait for the restart
184 * signal), and make sgen aware that even if a thread might be suspended, there
185 * would be cases where you cannot scan its stack/registers. That would in fact
186 * consist in removing the async suspend compensation, and treat the case directly
187 * in sgen. That's also how it was done in the sgen specific suspend code.
190 /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
191 current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
193 if (!current->suspend_can_continue)
194 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
197 Block the restart signal.
198 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
199 which might miss the signal and get stuck.
201 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
203 /* We're done suspending */
204 mono_threads_notify_initiator_of_suspend (current);
208 sigsuspend (&suspend_signal_mask);
209 } while (current->signal != restart_signal_num);
211 /* Unblock the restart signal. */
212 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
214 if (current->async_target) {
215 #if MONO_ARCH_HAS_MONO_CONTEXT
216 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
217 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
218 current->user_data = NULL;
219 current->async_target = NULL;
220 mono_monoctx_to_sigctx (&tmp, context);
222 g_error ("The new interruption machinery requires a working mono-context");
226 /* We're done resuming */
227 mono_threads_notify_initiator_of_resume (current);
230 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
236 abort_signal_handler (int _dummy, siginfo_t *info, void *context)
238 #if defined(__native_client__)
239 g_assert_not_reached ();
241 suspend_signal_handler (_dummy, info, context);
246 mono_threads_posix_init_signals (MonoThreadPosixInitSignals signals)
250 g_assert ((signals == MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART) ^ (signals == MONO_THREADS_POSIX_INIT_SIGNALS_ABORT));
252 sigemptyset (&signal_set);
255 case MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART: {
256 if (mono_thread_info_unified_management_enabled ()) {
257 suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
258 restart_signal_num = DEFAULT_RESTART_SIGNAL;
260 suspend_signal_num = suspend_signal_get ();
261 restart_signal_num = restart_signal_get ();
264 sigfillset (&suspend_signal_mask);
265 sigdelset (&suspend_signal_mask, restart_signal_num);
266 if (!mono_thread_info_unified_management_enabled ())
267 sigdelset (&suspend_signal_mask, mono_gc_get_suspend_signal ());
269 sigemptyset (&suspend_ack_signal_mask);
270 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
272 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
273 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
275 sigaddset (&signal_set, suspend_signal_num);
276 sigaddset (&signal_set, restart_signal_num);
280 case MONO_THREADS_POSIX_INIT_SIGNALS_ABORT: {
281 abort_signal_num = abort_signal_get ();
283 signal_add_handler (abort_signal_num, abort_signal_handler, 0);
285 sigaddset (&signal_set, abort_signal_num);
289 default: g_assert_not_reached ();
292 /* ensure all the new signals are unblocked */
293 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
297 mono_threads_posix_get_suspend_signal (void)
299 return suspend_signal_num;
303 mono_threads_posix_get_restart_signal (void)
305 return restart_signal_num;
309 mono_threads_posix_get_abort_signal (void)
311 return abort_signal_num;
314 #endif /* defined(USE_POSIX_BACKEND) */