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"
26 #include "mono-threads-coop.h"
29 mono_threads_suspend_search_alternative_signal (void)
31 #if !defined (SIGRTMIN)
32 g_error ("signal search only works with RTMIN");
35 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
36 for (i
= SIGRTMIN
+ 1; i
< SIGRTMAX
; ++i
) {
37 struct sigaction sinfo
;
38 sigaction (i
, NULL
, &sinfo
);
39 if (sinfo
.sa_handler
== SIG_DFL
&& (void*)sinfo
.sa_sigaction
== (void*)SIG_DFL
) {
43 g_error ("Could not find an available signal");
47 static int suspend_signal_num
= -1;
48 static int restart_signal_num
= -1;
49 static int abort_signal_num
= -1;
51 static sigset_t suspend_signal_mask
;
52 static sigset_t suspend_ack_signal_mask
;
55 signal_add_handler (int signo
, void (*handler
)(int, siginfo_t
*, void *), int flags
)
60 sa
.sa_sigaction
= handler
;
61 sigfillset (&sa
.sa_mask
);
62 sa
.sa_flags
= SA_SIGINFO
| flags
;
63 ret
= sigaction (signo
, &sa
, NULL
);
68 abort_signal_get (void)
70 #if defined(HOST_ANDROID)
72 #elif defined (__OpenBSD__)
74 #elif defined (SIGRTMIN)
75 static int abort_signum
= -1;
76 if (abort_signum
== -1)
77 abort_signum
= mono_threads_suspend_search_alternative_signal ();
79 #elif defined (SIGTTIN)
82 g_error ("unable to get abort signal");
87 suspend_signal_get (void)
89 #if defined(HOST_ANDROID)
91 #elif defined (SIGRTMIN)
92 static int suspend_signum
= -1;
93 if (suspend_signum
== -1)
94 suspend_signum
= mono_threads_suspend_search_alternative_signal ();
95 return suspend_signum
;
97 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
106 restart_signal_get (void)
108 #if defined(HOST_ANDROID)
110 #elif defined (SIGRTMIN)
111 static int restart_signum
= -1;
112 if (restart_signum
== -1)
113 restart_signum
= mono_threads_suspend_search_alternative_signal ();
114 return restart_signum
;
121 restart_signal_handler (int _dummy
, siginfo_t
*_info
, void *context
)
123 MonoThreadInfo
*info
;
124 int old_errno
= errno
;
126 info
= mono_thread_info_current ();
127 info
->signal
= restart_signal_num
;
132 suspend_signal_handler (int _dummy
, siginfo_t
*info
, void *context
)
134 int old_errno
= errno
;
135 int hp_save_index
= mono_hazard_pointer_save_for_signal_handler ();
137 MonoThreadInfo
*current
= mono_thread_info_current ();
139 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current
), (void*)current
->native_handle
);
140 if (current
->syscall_break_signal
) {
141 current
->syscall_break_signal
= FALSE
;
142 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current
));
143 mono_threads_notify_initiator_of_abort (current
);
147 /* Have we raced with self suspend? */
148 if (!mono_threads_transition_finish_async_suspend (current
)) {
149 current
->suspend_can_continue
= TRUE
;
150 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current
));
151 /* Under full preemptive suspend, there is no self suspension,
154 * Under full cooperative suspend, there is no signal, so no
157 * Under hybrid a blocking thread could race done/abort
158 * blocking with the signal handler running: if the done/abort
159 * blocking win, they will wait for a resume - the signal
160 * handler should notify the suspend initiator that the thread
161 * suspended, and then immediately return and let the thread
162 * continue waiting on the resume semaphore.
164 g_assert (mono_threads_is_hybrid_suspension_enabled ());
165 mono_threads_notify_initiator_of_suspend (current
);
170 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
171 * as the thread might have been attached without the domain or lmf having been
174 * One way to fix that is to keep the thread suspended (wait for the restart
175 * signal), and make sgen aware that even if a thread might be suspended, there
176 * would be cases where you cannot scan its stack/registers. That would in fact
177 * consist in removing the async suspend compensation, and treat the case directly
178 * in sgen. That's also how it was done in the sgen specific suspend code.
181 /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
182 current
->suspend_can_continue
= mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
], context
);
184 if (!current
->suspend_can_continue
)
185 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current
));
188 Block the restart signal.
189 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
190 which might miss the signal and get stuck.
192 pthread_sigmask (SIG_BLOCK
, &suspend_ack_signal_mask
, NULL
);
194 /* We're done suspending */
195 mono_threads_notify_initiator_of_suspend (current
);
199 sigsuspend (&suspend_signal_mask
);
200 } while (current
->signal
!= restart_signal_num
);
202 /* Unblock the restart signal. */
203 pthread_sigmask (SIG_UNBLOCK
, &suspend_ack_signal_mask
, NULL
);
205 if (current
->async_target
) {
206 #if MONO_ARCH_HAS_MONO_CONTEXT
207 MonoContext tmp
= current
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
].ctx
;
208 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp
, current
->async_target
, current
->user_data
);
209 current
->user_data
= NULL
;
210 current
->async_target
= NULL
;
211 mono_monoctx_to_sigctx (&tmp
, context
);
213 g_error ("The new interruption machinery requires a working mono-context");
217 /* We're done resuming */
218 mono_threads_notify_initiator_of_resume (current
);
221 mono_hazard_pointer_restore_for_signal_handler (hp_save_index
);
226 mono_threads_suspend_init_signals (void)
230 sigemptyset (&signal_set
);
232 /* add suspend signal */
233 suspend_signal_num
= suspend_signal_get ();
235 signal_add_handler (suspend_signal_num
, suspend_signal_handler
, SA_RESTART
);
237 sigaddset (&signal_set
, suspend_signal_num
);
239 /* add restart signal */
240 restart_signal_num
= restart_signal_get ();
242 sigfillset (&suspend_signal_mask
);
243 sigdelset (&suspend_signal_mask
, restart_signal_num
);
245 sigemptyset (&suspend_ack_signal_mask
);
246 sigaddset (&suspend_ack_signal_mask
, restart_signal_num
);
248 signal_add_handler (restart_signal_num
, restart_signal_handler
, SA_RESTART
);
250 sigaddset (&signal_set
, restart_signal_num
);
252 /* add abort signal */
253 abort_signal_num
= abort_signal_get ();
255 /* the difference between abort and suspend here is made by not
256 * passing SA_RESTART, meaning we won't restart the syscall when
257 * receiving a signal */
258 signal_add_handler (abort_signal_num
, suspend_signal_handler
, 0);
260 sigaddset (&signal_set
, abort_signal_num
);
262 /* ensure all the new signals are unblocked */
263 sigprocmask (SIG_UNBLOCK
, &signal_set
, NULL
);
266 On 32bits arm Android, signals with values >=32 are not usable as their headers ship a broken sigset_t.
267 See 5005c6f3fbc1da584c6a550281689cc23f59fe6d for more details.
270 g_assert (suspend_signal_num
< 32);
271 g_assert (restart_signal_num
< 32);
272 g_assert (abort_signal_num
< 32);
277 mono_threads_suspend_get_suspend_signal (void)
279 g_assert (suspend_signal_num
!= -1);
280 return suspend_signal_num
;
284 mono_threads_suspend_get_restart_signal (void)
286 g_assert (restart_signal_num
!= -1);
287 return restart_signal_num
;
291 mono_threads_suspend_get_abort_signal (void)
293 g_assert (abort_signal_num
!= -1);
294 return abort_signal_num
;
297 #endif /* defined(USE_POSIX_BACKEND) */