Revert some changes which don't have proper dependencies.
[mono-project.git] / mono / utils / mono-threads-posix-signals.c
blob3881504b781e68674b264e7e561b45a1e0feeea8
1 /**
2 * \file
3 * Shared facility for Posix signals support
5 * Author:
6 * Ludovic Henry (ludovic@gmail.com)
8 * (C) 2015 Xamarin, Inc
9 */
11 #include <config.h>
12 #include <glib.h>
14 #include "mono-threads.h"
16 #if defined(USE_POSIX_BACKEND)
18 #include <errno.h>
19 #include <mono/utils/mono-errno.h>
20 #include <signal.h>
22 #ifdef HAVE_ANDROID_LEGACY_SIGNAL_INLINES_H
23 #include <android/legacy_signal_inlines.h>
24 #endif
26 #include "mono-threads-debug.h"
27 #include "mono-threads-coop.h"
29 gint
30 mono_threads_suspend_search_alternative_signal (void)
32 #if !defined (SIGRTMIN)
33 g_error ("signal search only works with RTMIN");
34 #else
35 int i;
36 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
37 for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
38 struct sigaction sinfo;
39 sigaction (i, NULL, &sinfo);
40 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
41 return i;
44 g_error ("Could not find an available signal");
45 #endif
48 static int suspend_signal_num = -1;
49 static int restart_signal_num = -1;
50 static int abort_signal_num = -1;
52 static sigset_t suspend_signal_mask;
53 static sigset_t suspend_ack_signal_mask;
55 static void
56 signal_add_handler (int signo, void (*handler)(int, siginfo_t *, void *), int flags)
58 struct sigaction sa;
59 int ret;
61 sa.sa_sigaction = handler;
62 sigfillset (&sa.sa_mask);
63 sa.sa_flags = SA_SIGINFO | flags;
64 ret = sigaction (signo, &sa, NULL);
65 g_assert (ret != -1);
68 static int
69 abort_signal_get (void)
71 #if defined(HOST_ANDROID)
72 return SIGTTIN;
73 #elif defined (__OpenBSD__)
74 return SIGUSR1;
75 #elif defined (SIGRTMIN)
76 static int abort_signum = -1;
77 if (abort_signum == -1)
78 abort_signum = mono_threads_suspend_search_alternative_signal ();
79 return abort_signum;
80 #elif defined (SIGTTIN)
81 return SIGTTIN;
82 #else
83 g_error ("unable to get abort signal");
84 #endif
87 static int
88 suspend_signal_get (void)
90 #if defined(HOST_ANDROID)
91 return SIGPWR;
92 #elif defined (SIGRTMIN)
93 static int suspend_signum = -1;
94 if (suspend_signum == -1)
95 suspend_signum = mono_threads_suspend_search_alternative_signal ();
96 return suspend_signum;
97 #else
98 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
99 return SIGXFSZ;
100 #else
101 return SIGPWR;
102 #endif
103 #endif
106 static int
107 restart_signal_get (void)
109 #if defined(HOST_ANDROID)
110 return SIGXCPU;
111 #elif defined (SIGRTMIN)
112 static int restart_signum = -1;
113 if (restart_signum == -1)
114 restart_signum = mono_threads_suspend_search_alternative_signal ();
115 return restart_signum;
116 #else
117 return SIGXCPU;
118 #endif
121 static void
122 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
124 MonoThreadInfo *info;
125 int old_errno = errno;
127 info = mono_thread_info_current ();
128 info->signal = restart_signal_num;
129 mono_set_errno (old_errno);
132 static void
133 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
135 int old_errno = errno;
136 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
138 MonoThreadInfo *current = mono_thread_info_current ();
140 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
141 if (current->syscall_break_signal) {
142 current->syscall_break_signal = FALSE;
143 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
144 mono_threads_notify_initiator_of_abort (current);
145 goto done;
148 /* Have we raced with self suspend? */
149 if (!mono_threads_transition_finish_async_suspend (current)) {
150 current->suspend_can_continue = TRUE;
151 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
152 /* Under full preemptive suspend, there is no self suspension,
153 * so no race.
155 * Under full cooperative suspend, there is no signal, so no
156 * race.
158 * Under hybrid a blocking thread could race done/abort
159 * blocking with the signal handler running: if the done/abort
160 * blocking win, they will wait for a resume - the signal
161 * handler should notify the suspend initiator that the thread
162 * suspended, and then immediately return and let the thread
163 * continue waiting on the resume semaphore.
165 g_assert (mono_threads_is_hybrid_suspension_enabled ());
166 mono_threads_notify_initiator_of_suspend (current);
167 goto done;
171 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
172 * as the thread might have been attached without the domain or lmf having been
173 * initialized yet.
175 * One way to fix that is to keep the thread suspended (wait for the restart
176 * signal), and make sgen aware that even if a thread might be suspended, there
177 * would be cases where you cannot scan its stack/registers. That would in fact
178 * consist in removing the async suspend compensation, and treat the case directly
179 * in sgen. That's also how it was done in the sgen specific suspend code.
182 /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
183 current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
185 if (!current->suspend_can_continue)
186 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
189 Block the restart signal.
190 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
191 which might miss the signal and get stuck.
193 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
195 /* We're done suspending */
196 mono_threads_notify_initiator_of_suspend (current);
198 do {
199 current->signal = 0;
200 sigsuspend (&suspend_signal_mask);
201 } while (current->signal != restart_signal_num);
203 /* Unblock the restart signal. */
204 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
206 if (current->async_target) {
207 #if MONO_ARCH_HAS_MONO_CONTEXT
208 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
209 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
210 current->user_data = NULL;
211 current->async_target = NULL;
212 mono_monoctx_to_sigctx (&tmp, context);
213 #else
214 g_error ("The new interruption machinery requires a working mono-context");
215 #endif
218 /* We're done resuming */
219 mono_threads_notify_initiator_of_resume (current);
221 done:
222 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
223 mono_set_errno (old_errno);
226 void
227 mono_threads_suspend_init_signals (void)
229 sigset_t signal_set;
231 sigemptyset (&signal_set);
233 /* add suspend signal */
234 suspend_signal_num = suspend_signal_get ();
236 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
238 sigaddset (&signal_set, suspend_signal_num);
240 /* add restart signal */
241 restart_signal_num = restart_signal_get ();
243 sigfillset (&suspend_signal_mask);
244 sigdelset (&suspend_signal_mask, restart_signal_num);
246 sigemptyset (&suspend_ack_signal_mask);
247 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
249 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
251 sigaddset (&signal_set, restart_signal_num);
253 /* add abort signal */
254 abort_signal_num = abort_signal_get ();
256 /* the difference between abort and suspend here is made by not
257 * passing SA_RESTART, meaning we won't restart the syscall when
258 * receiving a signal */
259 signal_add_handler (abort_signal_num, suspend_signal_handler, 0);
261 sigaddset (&signal_set, abort_signal_num);
263 /* ensure all the new signals are unblocked */
264 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
267 On 32bits arm Android, signals with values >=32 are not usable as their headers ship a broken sigset_t.
268 See 5005c6f3fbc1da584c6a550281689cc23f59fe6d for more details.
270 #ifdef HOST_ANDROID
271 g_assert (suspend_signal_num < 32);
272 g_assert (restart_signal_num < 32);
273 g_assert (abort_signal_num < 32);
274 #endif
277 gint
278 mono_threads_suspend_get_suspend_signal (void)
280 g_assert (suspend_signal_num != -1);
281 return suspend_signal_num;
284 gint
285 mono_threads_suspend_get_restart_signal (void)
287 g_assert (restart_signal_num != -1);
288 return restart_signal_num;
291 gint
292 mono_threads_suspend_get_abort_signal (void)
294 g_assert (abort_signal_num != -1);
295 return abort_signal_num;
298 #endif /* defined(USE_POSIX_BACKEND) */