3 * Shared facility for Posix signals support
6 * Ludovic Henry (ludovic@gmail.com)
8 * (C) 2015 Xamarin, Inc
14 #include "mono-threads.h"
16 #if defined(USE_POSIX_BACKEND)
21 #ifdef HAVE_ANDROID_LEGACY_SIGNAL_INLINES_H
22 #include <android/legacy_signal_inlines.h>
25 #include "mono-threads-debug.h"
28 mono_threads_suspend_search_alternative_signal (void)
30 #if !defined (SIGRTMIN)
31 g_error ("signal search only works with RTMIN");
34 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
35 for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
36 struct sigaction sinfo;
37 sigaction (i, NULL, &sinfo);
38 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
42 g_error ("Could not find an available signal");
46 static int suspend_signal_num = -1;
47 static int restart_signal_num = -1;
48 static int abort_signal_num = -1;
50 static sigset_t suspend_signal_mask;
51 static sigset_t suspend_ack_signal_mask;
54 signal_add_handler (int signo, void (*handler)(int, siginfo_t *, void *), int flags)
59 sa.sa_sigaction = handler;
60 sigfillset (&sa.sa_mask);
61 sa.sa_flags = SA_SIGINFO | flags;
62 ret = sigaction (signo, &sa, NULL);
67 abort_signal_get (void)
69 #if defined(HOST_ANDROID)
71 #elif defined (SIGRTMIN)
72 static int abort_signum = -1;
73 if (abort_signum == -1)
74 abort_signum = mono_threads_suspend_search_alternative_signal ();
76 #elif defined (SIGTTIN)
79 g_error ("unable to get abort signal");
84 suspend_signal_get (void)
86 #if defined(HOST_ANDROID)
88 #elif defined (SIGRTMIN)
89 static int suspend_signum = -1;
90 if (suspend_signum == -1)
91 suspend_signum = mono_threads_suspend_search_alternative_signal ();
92 return suspend_signum;
94 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
103 restart_signal_get (void)
105 #if defined(HOST_ANDROID)
107 #elif defined (SIGRTMIN)
108 static int restart_signum = -1;
109 if (restart_signum == -1)
110 restart_signum = mono_threads_suspend_search_alternative_signal ();
111 return restart_signum;
118 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
120 MonoThreadInfo *info;
121 int old_errno = errno;
123 info = mono_thread_info_current ();
124 info->signal = restart_signal_num;
129 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
131 int old_errno = errno;
132 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
134 MonoThreadInfo *current = mono_thread_info_current ();
136 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
137 if (current->syscall_break_signal) {
138 current->syscall_break_signal = FALSE;
139 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
140 mono_threads_notify_initiator_of_abort (current);
144 /* Have we raced with self suspend? */
145 if (!mono_threads_transition_finish_async_suspend (current)) {
146 current->suspend_can_continue = TRUE;
147 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
152 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
153 * as the thread might have been attached without the domain or lmf having been
156 * One way to fix that is to keep the thread suspended (wait for the restart
157 * signal), and make sgen aware that even if a thread might be suspended, there
158 * would be cases where you cannot scan its stack/registers. That would in fact
159 * consist in removing the async suspend compensation, and treat the case directly
160 * in sgen. That's also how it was done in the sgen specific suspend code.
163 /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
164 current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
166 if (!current->suspend_can_continue)
167 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
170 Block the restart signal.
171 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
172 which might miss the signal and get stuck.
174 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
176 /* We're done suspending */
177 mono_threads_notify_initiator_of_suspend (current);
181 sigsuspend (&suspend_signal_mask);
182 } while (current->signal != restart_signal_num);
184 /* Unblock the restart signal. */
185 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
187 if (current->async_target) {
188 #if MONO_ARCH_HAS_MONO_CONTEXT
189 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
190 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
191 current->user_data = NULL;
192 current->async_target = NULL;
193 mono_monoctx_to_sigctx (&tmp, context);
195 g_error ("The new interruption machinery requires a working mono-context");
199 /* We're done resuming */
200 mono_threads_notify_initiator_of_resume (current);
203 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
208 mono_threads_suspend_init_signals (void)
212 sigemptyset (&signal_set);
214 /* add suspend signal */
215 suspend_signal_num = suspend_signal_get ();
217 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
219 sigaddset (&signal_set, suspend_signal_num);
221 /* add restart signal */
222 restart_signal_num = restart_signal_get ();
224 sigfillset (&suspend_signal_mask);
225 sigdelset (&suspend_signal_mask, restart_signal_num);
227 sigemptyset (&suspend_ack_signal_mask);
228 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
230 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
232 sigaddset (&signal_set, restart_signal_num);
234 /* add abort signal */
235 abort_signal_num = abort_signal_get ();
237 /* the difference between abort and suspend here is made by not
238 * passing SA_RESTART, meaning we won't restart the syscall when
239 * receiving a signal */
240 signal_add_handler (abort_signal_num, suspend_signal_handler, 0);
242 sigaddset (&signal_set, abort_signal_num);
244 /* ensure all the new signals are unblocked */
245 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
248 On 32bits arm Android, signals with values >=32 are not usable as their headers ship a broken sigset_t.
249 See 5005c6f3fbc1da584c6a550281689cc23f59fe6d for more details.
252 g_assert (suspend_signal_num < 32);
253 g_assert (restart_signal_num < 32);
254 g_assert (abort_signal_num < 32);
259 mono_threads_suspend_get_suspend_signal (void)
261 g_assert (suspend_signal_num != -1);
262 return suspend_signal_num;
266 mono_threads_suspend_get_restart_signal (void)
268 g_assert (restart_signal_num != -1);
269 return restart_signal_num;
273 mono_threads_suspend_get_abort_signal (void)
275 g_assert (abort_signal_num != -1);
276 return abort_signal_num;
279 #endif /* defined(USE_POSIX_BACKEND) */