[tools] Add nuget-hash-extractor tool to help produce the runtime ignored assemblies...
[mono-project.git] / mono / utils / mono-threads-posix-signals.c
blobc19ef8fe402d6dc557057a5088bdc133bec9c218
1 /*
2 * mono-threads-posix-signals.c: Shared facility for Posix signals support
4 * Author:
5 * Ludovic Henry (ludovic@gmail.com)
7 * (C) 2015 Xamarin, Inc
8 */
10 #include <config.h>
11 #include <glib.h>
13 #include "mono-threads.h"
15 #if defined(USE_POSIX_BACKEND)
17 #include <errno.h>
18 #include <signal.h>
20 #include "mono-threads-debug.h"
22 gint
23 mono_threads_suspend_search_alternative_signal (void)
25 #if !defined (SIGRTMIN)
26 g_error ("signal search only works with RTMIN");
27 #else
28 int i;
29 /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
30 for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
31 struct sigaction sinfo;
32 sigaction (i, NULL, &sinfo);
33 if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
34 return i;
37 g_error ("Could not find an available signal");
38 #endif
41 #ifndef __native_client__
43 static int suspend_signal_num = -1;
44 static int restart_signal_num = -1;
45 static int abort_signal_num = -1;
47 static sigset_t suspend_signal_mask;
48 static sigset_t suspend_ack_signal_mask;
50 static void
51 signal_add_handler (int signo, void (*handler)(int, siginfo_t *, void *), int flags)
53 struct sigaction sa;
54 int ret;
56 sa.sa_sigaction = handler;
57 sigfillset (&sa.sa_mask);
58 sa.sa_flags = SA_SIGINFO | flags;
59 ret = sigaction (signo, &sa, NULL);
60 g_assert (ret != -1);
63 static int
64 abort_signal_get (void)
66 #if defined(PLATFORM_ANDROID)
67 return SIGTTIN;
68 #elif defined (SIGRTMIN)
69 static int abort_signum = -1;
70 if (abort_signum == -1)
71 abort_signum = mono_threads_suspend_search_alternative_signal ();
72 return abort_signum;
73 #elif defined (SIGTTIN)
74 return SIGTTIN;
75 #else
76 g_error ("unable to get abort signal");
77 #endif
80 static int
81 suspend_signal_get (void)
83 #ifdef SIGRTMIN
84 static int suspend_signum = -1;
85 if (suspend_signum == -1)
86 suspend_signum = mono_threads_suspend_search_alternative_signal ();
87 return suspend_signum;
88 #else
89 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
90 return SIGXFSZ;
91 #else
92 return SIGPWR;
93 #endif
94 #endif
97 static int
98 restart_signal_get (void)
100 #ifdef SIGRTMIN
101 static int restart_signum = -1;
102 if (restart_signum == -1)
103 restart_signum = mono_threads_suspend_search_alternative_signal ();
104 return restart_signum;
105 #else
106 return SIGXCPU;
107 #endif
110 static void
111 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
113 MonoThreadInfo *info;
114 int old_errno = errno;
116 info = mono_thread_info_current ();
117 info->signal = restart_signal_num;
118 errno = old_errno;
121 static void
122 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
124 int old_errno = errno;
125 int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
127 MonoThreadInfo *current = mono_thread_info_current ();
129 THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
130 if (current->syscall_break_signal) {
131 current->syscall_break_signal = FALSE;
132 THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
133 mono_threads_notify_initiator_of_abort (current);
134 goto done;
137 /* Have we raced with self suspend? */
138 if (!mono_threads_transition_finish_async_suspend (current)) {
139 current->suspend_can_continue = TRUE;
140 THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
141 goto done;
145 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
146 * as the thread might have been attached without the domain or lmf having been
147 * initialized yet.
149 * One way to fix that is to keep the thread suspended (wait for the restart
150 * signal), and make sgen aware that even if a thread might be suspended, there
151 * would be cases where you cannot scan its stack/registers. That would in fact
152 * consist in removing the async suspend compensation, and treat the case directly
153 * in sgen. That's also how it was done in the sgen specific suspend code.
156 /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
157 current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
159 if (!current->suspend_can_continue)
160 THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
163 Block the restart signal.
164 We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
165 which might miss the signal and get stuck.
167 pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
169 /* We're done suspending */
170 mono_threads_notify_initiator_of_suspend (current);
172 do {
173 current->signal = 0;
174 sigsuspend (&suspend_signal_mask);
175 } while (current->signal != restart_signal_num);
177 /* Unblock the restart signal. */
178 pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
180 if (current->async_target) {
181 #if MONO_ARCH_HAS_MONO_CONTEXT
182 MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
183 mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
184 current->user_data = NULL;
185 current->async_target = NULL;
186 mono_monoctx_to_sigctx (&tmp, context);
187 #else
188 g_error ("The new interruption machinery requires a working mono-context");
189 #endif
192 /* We're done resuming */
193 mono_threads_notify_initiator_of_resume (current);
195 done:
196 mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
197 errno = old_errno;
200 void
201 mono_threads_suspend_init_signals (void)
203 sigset_t signal_set;
205 sigemptyset (&signal_set);
207 /* add suspend signal */
208 suspend_signal_num = suspend_signal_get ();
210 signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
212 sigaddset (&signal_set, suspend_signal_num);
214 /* add restart signal */
215 restart_signal_num = restart_signal_get ();
217 sigfillset (&suspend_signal_mask);
218 sigdelset (&suspend_signal_mask, restart_signal_num);
220 sigemptyset (&suspend_ack_signal_mask);
221 sigaddset (&suspend_ack_signal_mask, restart_signal_num);
223 signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
225 sigaddset (&signal_set, restart_signal_num);
227 /* add abort signal */
228 abort_signal_num = abort_signal_get ();
230 /* the difference between abort and suspend here is made by not
231 * passing SA_RESTART, meaning we won't restart the syscall when
232 * receiving a signal */
233 signal_add_handler (abort_signal_num, suspend_signal_handler, 0);
235 sigaddset (&signal_set, abort_signal_num);
237 /* ensure all the new signals are unblocked */
238 sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
241 gint
242 mono_threads_suspend_get_suspend_signal (void)
244 g_assert (suspend_signal_num != -1);
245 return suspend_signal_num;
248 gint
249 mono_threads_suspend_get_restart_signal (void)
251 g_assert (restart_signal_num != -1);
252 return restart_signal_num;
255 gint
256 mono_threads_suspend_get_abort_signal (void)
258 g_assert (abort_signal_num != -1);
259 return abort_signal_num;
262 #else
264 void
265 mono_threads_suspend_init_signals (void)
267 g_assert_not_reached ();
270 gint
271 mono_threads_suspend_get_suspend_signal (void)
273 return -1;
276 gint
277 mono_threads_suspend_get_restart_signal (void)
279 return -1;
282 gint
283 mono_threads_suspend_get_abort_signal (void)
285 return -1;
288 #endif /* __native_client__ */
291 #endif /* defined(USE_POSIX_BACKEND) */