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 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
= 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
= 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
= 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", 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", 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", current
);
178 ret
= mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (¤t
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
], context
);
180 /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
181 current
->suspend_can_continue
= ret
;
184 Block the restart signal.
185 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
186 which might miss the signal and get stuck.
188 pthread_sigmask (SIG_BLOCK
, &suspend_ack_signal_mask
, NULL
);
190 /* This thread is doomed, all we can do is give up and let the suspender recover. */
192 THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current
);
193 mono_threads_transition_async_suspend_compensation (current
);
195 /* We're done suspending */
196 mono_threads_notify_initiator_of_suspend (current
);
198 /* Unblock the restart signal. */
199 pthread_sigmask (SIG_UNBLOCK
, &suspend_ack_signal_mask
, NULL
);
204 /* We're done suspending */
205 mono_threads_notify_initiator_of_suspend (current
);
209 sigsuspend (&suspend_signal_mask
);
210 } while (current
->signal
!= restart_signal_num
);
212 /* Unblock the restart signal. */
213 pthread_sigmask (SIG_UNBLOCK
, &suspend_ack_signal_mask
, NULL
);
215 if (current
->async_target
) {
216 #if MONO_ARCH_HAS_MONO_CONTEXT
217 MonoContext tmp
= current
->thread_saved_state
[ASYNC_SUSPEND_STATE_INDEX
].ctx
;
218 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp
, current
->async_target
, current
->user_data
);
219 current
->user_data
= NULL
;
220 current
->async_target
= 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) */