Prepare Mono for Android NDK with unified headers (#5680)
[mono.git] / mono / utils / mono-threads-posix-signals.c
index 155f830a0fa15bcda4b9e803617f938c6c513889..b39277393f5d87a979793202fcae936459086a32 100644 (file)
@@ -1,5 +1,6 @@
-/*
- * mono-threads-posix-signals.c: Shared facility for Posix signals support
+/**
+ * \file
+ * Shared facility for Posix signals support
  *
  * Author:
  *     Ludovic Henry (ludovic@gmail.com)
 #include <errno.h>
 #include <signal.h>
 
-#include "mono-threads-posix-signals.h"
-
-#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
-#define DEFAULT_SUSPEND_SIGNAL SIGXFSZ
-#else
-#define DEFAULT_SUSPEND_SIGNAL SIGPWR
+#ifdef HAVE_ANDROID_LEGACY_SIGNAL_INLINES_H
+#include <android/legacy_signal_inlines.h>
 #endif
-#define DEFAULT_RESTART_SIGNAL SIGXCPU
-
-static int suspend_signal_num;
-static int restart_signal_num;
-static int abort_signal_num;
-
-static sigset_t suspend_signal_mask;
-static sigset_t suspend_ack_signal_mask;
 
-//Can't avoid the circular dep on this. Will be gone pretty soon
-extern int mono_gc_get_suspend_signal (void);
+#include "mono-threads-debug.h"
 
-static int
-signal_search_alternative (int min_signal)
+gint
+mono_threads_suspend_search_alternative_signal (void)
 {
 #if !defined (SIGRTMIN)
        g_error ("signal search only works with RTMIN");
 #else
        int i;
        /* we try to avoid SIGRTMIN and any one that might have been set already, see bug #75387 */
-       for (i = MAX (min_signal, SIGRTMIN) + 1; i < SIGRTMAX; ++i) {
+       for (i = SIGRTMIN + 1; i < SIGRTMAX; ++i) {
                struct sigaction sinfo;
                sigaction (i, NULL, &sinfo);
                if (sinfo.sa_handler == SIG_DFL && (void*)sinfo.sa_sigaction == (void*)SIG_DFL) {
@@ -55,115 +43,100 @@ signal_search_alternative (int min_signal)
 #endif
 }
 
+static int suspend_signal_num = -1;
+static int restart_signal_num = -1;
+static int abort_signal_num = -1;
+
+static sigset_t suspend_signal_mask;
+static sigset_t suspend_ack_signal_mask;
+
 static void
-signal_add_handler (int signo, gpointer handler, int flags)
+signal_add_handler (int signo, void (*handler)(int, siginfo_t *, void *), int flags)
 {
-#if !defined(__native_client__)
-       /*FIXME, move the code from mini to utils and do the right thing!*/
        struct sigaction sa;
-       struct sigaction previous_sa;
        int ret;
 
-       sa.sa_sigaction = (void (*)(int, siginfo_t *, void *))handler;
+       sa.sa_sigaction = handler;
        sigfillset (&sa.sa_mask);
-
        sa.sa_flags = SA_SIGINFO | flags;
-       ret = sigaction (signo, &sa, &previous_sa);
-
+       ret = sigaction (signo, &sa, NULL);
        g_assert (ret != -1);
-#endif
 }
 
 static int
-suspend_signal_get (void)
+abort_signal_get (void)
 {
-#if defined(PLATFORM_ANDROID)
-       return SIGUNUSED;
-#elif !defined (SIGRTMIN)
-#ifdef SIGUSR1
-       return SIGUSR1;
-#else
-       return -1;
-#endif /* SIGUSR1 */
+#if defined(HOST_ANDROID)
+       return SIGTTIN;
+#elif defined (SIGRTMIN)
+       static int abort_signum = -1;
+       if (abort_signum == -1)
+               abort_signum = mono_threads_suspend_search_alternative_signal ();
+       return abort_signum;
+#elif defined (SIGTTIN)
+       return SIGTTIN;
 #else
-       static int suspend_signum = -1;
-       if (suspend_signum == -1)
-               suspend_signum = signal_search_alternative (-1);
-       return suspend_signum;
-#endif /* SIGRTMIN */
+       g_error ("unable to get abort signal");
+#endif
 }
 
 static int
-restart_signal_get (void)
+suspend_signal_get (void)
 {
-#if defined(PLATFORM_ANDROID)
-       return SIGTTOU;
-#elif !defined (SIGRTMIN)
-#ifdef SIGUSR2
-       return SIGUSR2;
+#if defined(HOST_ANDROID)
+       return SIGPWR;
+#elif defined (SIGRTMIN)
+       static int suspend_signum = -1;
+       if (suspend_signum == -1)
+               suspend_signum = mono_threads_suspend_search_alternative_signal ();
+       return suspend_signum;
 #else
-       return -1;
-#endif /* SIGUSR1 */
+#if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       return SIGXFSZ;
 #else
-       static int resume_signum = -1;
-       if (resume_signum == -1)
-               resume_signum = signal_search_alternative (suspend_signal_get () + 1);
-       return resume_signum;
-#endif /* SIGRTMIN */
+       return SIGPWR;
+#endif
+#endif
 }
 
-
 static int
-abort_signal_get (void)
+restart_signal_get (void)
 {
-#if defined(PLATFORM_ANDROID)
-       return SIGTTIN;
-#elif !defined (SIGRTMIN)
-#ifdef SIGTTIN
-       return SIGTTIN;
+#if defined(HOST_ANDROID)
+       return SIGXCPU;
+#elif defined (SIGRTMIN)
+       static int restart_signum = -1;
+       if (restart_signum == -1)
+               restart_signum = mono_threads_suspend_search_alternative_signal ();
+       return restart_signum;
 #else
-       return -1;
-#endif /* SIGRTMIN */
-#else
-       static int abort_signum = -1;
-       if (abort_signum == -1)
-               abort_signum = signal_search_alternative (restart_signal_get () + 1);
-       return abort_signum;
-#endif /* SIGRTMIN */
+       return SIGXCPU;
+#endif
 }
 
 static void
 restart_signal_handler (int _dummy, siginfo_t *_info, void *context)
 {
-#if defined(__native_client__)
-       g_assert_not_reached ();
-#else
        MonoThreadInfo *info;
        int old_errno = errno;
 
        info = mono_thread_info_current ();
        info->signal = restart_signal_num;
        errno = old_errno;
-#endif
 }
 
 static void
 suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
 {
-#if defined(__native_client__)
-       g_assert_not_reached ();
-#else
        int old_errno = errno;
        int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();
 
-
        MonoThreadInfo *current = mono_thread_info_current ();
-       gboolean ret;
 
-       THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
+       THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
        if (current->syscall_break_signal) {
                current->syscall_break_signal = FALSE;
-               THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
+               THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
                mono_threads_notify_initiator_of_abort (current);
                goto done;
        }
@@ -171,14 +144,27 @@ suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
        /* Have we raced with self suspend? */
        if (!mono_threads_transition_finish_async_suspend (current)) {
                current->suspend_can_continue = TRUE;
-               THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
+               THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
                goto done;
        }
 
-       ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
-
-       /* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
-       current->suspend_can_continue = ret;
+       /*
+        * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
+        * as the thread might have been attached without the domain or lmf having been
+        * initialized yet.
+        *
+        * One way to fix that is to keep the thread suspended (wait for the restart
+        * signal), and make sgen aware that even if a thread might be suspended, there
+        * would be cases where you cannot scan its stack/registers. That would in fact
+        * consist in removing the async suspend compensation, and treat the case directly
+        * in sgen. That's also how it was done in the sgen specific suspend code.
+        */
+
+       /* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
+       current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);
+
+       if (!current->suspend_can_continue)
+               THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));
 
        /*
        Block the restart signal.
@@ -187,20 +173,6 @@ suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
        */
        pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);
 
-       /* This thread is doomed, all we can do is give up and let the suspender recover. */
-       if (!ret) {
-               THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
-               mono_threads_transition_async_suspend_compensation (current);
-
-               /* We're done suspending */
-               mono_threads_notify_initiator_of_suspend (current);
-
-               /* Unblock the restart signal. */
-               pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);
-
-               goto done;
-       }
-
        /* We're done suspending */
        mono_threads_notify_initiator_of_suspend (current);
 
@@ -230,85 +202,77 @@ suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
 done:
        mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
        errno = old_errno;
-#endif
-}
-
-static void
-abort_signal_handler (int _dummy, siginfo_t *info, void *context)
-{
-#if defined(__native_client__)
-       g_assert_not_reached ();
-#else
-       suspend_signal_handler (_dummy, info, context);
-#endif
 }
 
 void
-mono_threads_posix_init_signals (MonoThreadPosixInitSignals signals)
+mono_threads_suspend_init_signals (void)
 {
        sigset_t signal_set;
 
-       g_assert ((signals == MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART) ^ (signals == MONO_THREADS_POSIX_INIT_SIGNALS_ABORT));
-
        sigemptyset (&signal_set);
 
-       switch (signals) {
-       case MONO_THREADS_POSIX_INIT_SIGNALS_SUSPEND_RESTART: {
-               if (mono_thread_info_unified_management_enabled ()) {
-                       suspend_signal_num = DEFAULT_SUSPEND_SIGNAL;
-                       restart_signal_num = DEFAULT_RESTART_SIGNAL;
-               } else {
-                       suspend_signal_num = suspend_signal_get ();
-                       restart_signal_num = restart_signal_get ();
-               }
+       /* add suspend signal */
+       suspend_signal_num = suspend_signal_get ();
 
-               sigfillset (&suspend_signal_mask);
-               sigdelset (&suspend_signal_mask, restart_signal_num);
-               if (!mono_thread_info_unified_management_enabled ())
-                       sigdelset (&suspend_signal_mask, mono_gc_get_suspend_signal ());
+       signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
 
-               sigemptyset (&suspend_ack_signal_mask);
-               sigaddset (&suspend_ack_signal_mask, restart_signal_num);
+       sigaddset (&signal_set, suspend_signal_num);
 
-               signal_add_handler (suspend_signal_num, suspend_signal_handler, SA_RESTART);
-               signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
+       /* add restart signal */
+       restart_signal_num = restart_signal_get ();
 
-               sigaddset (&signal_set, suspend_signal_num);
-               sigaddset (&signal_set, restart_signal_num);
+       sigfillset (&suspend_signal_mask);
+       sigdelset (&suspend_signal_mask, restart_signal_num);
 
-               break;
-       }
-       case MONO_THREADS_POSIX_INIT_SIGNALS_ABORT: {
-               abort_signal_num = abort_signal_get ();
+       sigemptyset (&suspend_ack_signal_mask);
+       sigaddset (&suspend_ack_signal_mask, restart_signal_num);
 
-               signal_add_handler (abort_signal_num, abort_signal_handler, 0);
+       signal_add_handler (restart_signal_num, restart_signal_handler, SA_RESTART);
 
-               sigaddset (&signal_set, abort_signal_num);
+       sigaddset (&signal_set, restart_signal_num);
 
-               break;
-       }
-       default: g_assert_not_reached ();
-       }
+       /* add abort signal */
+       abort_signal_num = abort_signal_get ();
+
+       /* the difference between abort and suspend here is made by not
+        * passing SA_RESTART, meaning we won't restart the syscall when
+        * receiving a signal */
+       signal_add_handler (abort_signal_num, suspend_signal_handler, 0);
+
+       sigaddset (&signal_set, abort_signal_num);
 
        /* ensure all the new signals are unblocked */
        sigprocmask (SIG_UNBLOCK, &signal_set, NULL);
+
+       /*
+       On 32bits arm Android, signals with values >=32 are not usable as their headers ship a broken sigset_t.
+       See 5005c6f3fbc1da584c6a550281689cc23f59fe6d for more details.
+       */
+#ifdef HOST_ANDROID
+       g_assert (suspend_signal_num < 32);
+       g_assert (restart_signal_num < 32);
+       g_assert (abort_signal_num < 32);
+#endif
 }
 
 gint
-mono_threads_posix_get_suspend_signal (void)
+mono_threads_suspend_get_suspend_signal (void)
 {
+       g_assert (suspend_signal_num != -1);
        return suspend_signal_num;
 }
 
 gint
-mono_threads_posix_get_restart_signal (void)
+mono_threads_suspend_get_restart_signal (void)
 {
+       g_assert (restart_signal_num != -1);
        return restart_signal_num;
 }
 
 gint
-mono_threads_posix_get_abort_signal (void)
+mono_threads_suspend_get_abort_signal (void)
 {
+       g_assert (abort_signal_num != -1);
        return abort_signal_num;
 }