[profiler] Avoid a crash at shutdown by installing the assembly unload hook for the...
[mono.git] / mono / utils / mono-threads-coop.c
index b44719109ffc277ad377942a4b219b21a92d8408..03e0aba0bb160e2afbbb4847d79f044076cb9823 100644 (file)
@@ -1,4 +1,4 @@
-/*
+ /*
  * mono-threads.c: Coop threading
  *
  * Author:
@@ -7,6 +7,13 @@
  * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
  */
 
+#include <config.h>
+
+/* enable pthread extensions */
+#ifdef TARGET_MACH
+#define _DARWIN_C_SOURCE
+#endif
+
 #include <mono/utils/mono-compiler.h>
 #include <mono/utils/mono-semaphore.h>
 #include <mono/utils/mono-threads.h>
 #include <mono/utils/mono-mmap.h>
 #include <mono/utils/atomic.h>
 #include <mono/utils/mono-time.h>
+#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-threads-coop.h>
+
+#ifdef TARGET_OSX
+#include <mono/utils/mach-support.h>
+#endif
 
-#ifdef USE_COOP_BACKEND
+#ifdef _MSC_VER
+// TODO: Find MSVC replacement for __builtin_unwind_init
+#define SAVE_REGS_ON_STACK g_assert_not_reached ();
+#else 
+#define SAVE_REGS_ON_STACK __builtin_unwind_init ();
+#endif
 
 volatile size_t mono_polling_required;
 
+static int coop_reset_blocking_count;
+static int coop_try_blocking_count;
+static int coop_do_blocking_count;
+static int coop_do_polling_count;
+static int coop_save_count;
+
 void
 mono_threads_state_poll (void)
 {
        MonoThreadInfo *info;
 
+       g_assert (mono_threads_is_coop_enabled ());
+
+       ++coop_do_polling_count;
+
        info = mono_thread_info_current_unchecked ();
        if (!info)
                return;
@@ -35,7 +63,8 @@ mono_threads_state_poll (void)
        if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
                return;
 
-       g_assert (mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL));
+       ++coop_save_count;
+       mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
 
        /* commit the saved state and notify others if needed */
        switch (mono_threads_transition_state_poll (info)) {
@@ -51,11 +80,45 @@ mono_threads_state_poll (void)
        }
 }
 
+static void *
+return_stack_ptr ()
+{
+       int i;
+       return &i;
+}
+
+static void
+copy_stack_data (MonoThreadInfo *info, void* stackdata_begin)
+{
+       MonoThreadUnwindState *state;
+       int stackdata_size;
+       void* stackdata_end = return_stack_ptr ();
+
+       SAVE_REGS_ON_STACK;
+
+       state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
+
+       stackdata_size = (char*)stackdata_begin - (char*)stackdata_end;
+       if (stackdata_size <= 0)
+               g_error ("stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", stackdata_size, stackdata_begin, stackdata_end);
+
+       g_byte_array_set_size (info->stackdata, stackdata_size);
+       state->gc_stackdata = info->stackdata->data;
+       memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
+
+       state->gc_stackdata_size = stackdata_size;
+}
+
 void*
-mono_threads_prepare_blocking (void)
+mono_threads_prepare_blocking (void* stackdata)
 {
        MonoThreadInfo *info;
 
+       if (!mono_threads_is_coop_enabled ())
+               return NULL;
+
+       ++coop_do_blocking_count;
+
        info = mono_thread_info_current_unchecked ();
        /* If the thread is not attached, it doesn't make sense prepare for suspend. */
        if (!info || !mono_thread_info_is_live (info)) {
@@ -63,12 +126,11 @@ mono_threads_prepare_blocking (void)
                return NULL;
        }
 
+       copy_stack_data (info, stackdata);
+
 retry:
-       /*The JIT might not be able to save*/
-       if (!mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL)) {
-               THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p to save thread state\n", mono_thread_info_get_tid (info));
-               return NULL;
-       }
+       ++coop_save_count;
+       mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
 
        switch (mono_threads_transition_do_blocking (info)) {
        case DoBlockingContinue:
@@ -82,11 +144,15 @@ retry:
 }
 
 void
-mono_threads_finish_blocking (void *cookie)
+mono_threads_finish_blocking (void *cookie, void* stackdata)
 {
        static gboolean warned_about_bad_transition;
-       MonoThreadInfo *info = cookie;
+       MonoThreadInfo *info;
+
+       if (!mono_threads_is_coop_enabled ())
+               return;
 
+       info = cookie;
        if (!info)
                return;
 
@@ -114,14 +180,22 @@ mono_threads_finish_blocking (void *cookie)
 
 
 void*
-mono_threads_reset_blocking_start (void)
+mono_threads_reset_blocking_start (void* stackdata)
 {
-       MonoThreadInfo *info = mono_thread_info_current_unchecked ();
+       MonoThreadInfo *info;
 
+       if (!mono_threads_is_coop_enabled ())
+               return NULL;
+
+       ++coop_reset_blocking_count;
+
+       info = mono_thread_info_current_unchecked ();
        /* If the thread is not attached, it doesn't make sense prepare for suspend. */
        if (!info || !mono_thread_info_is_live (info))
                return NULL;
 
+       copy_stack_data (info, stackdata);
+
        switch (mono_threads_transition_abort_blocking (info)) {
        case AbortBlockingIgnore:
                info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
@@ -141,22 +215,31 @@ mono_threads_reset_blocking_start (void)
 }
 
 void
-mono_threads_reset_blocking_end (void *cookie)
+mono_threads_reset_blocking_end (void *cookie, void* stackdata)
 {
-       MonoThreadInfo *info = cookie;
+       MonoThreadInfo *info;
+
+       if (!mono_threads_is_coop_enabled ())
+               return;
 
+       info = cookie;
        if (!info)
                return;
 
        g_assert (info == mono_thread_info_current_unchecked ());
-       mono_threads_prepare_blocking ();
+       mono_threads_prepare_blocking (stackdata);
 }
 
 void*
-mono_threads_try_prepare_blocking (void)
+mono_threads_try_prepare_blocking (void* stackdata)
 {
        MonoThreadInfo *info;
 
+       if (!mono_threads_is_coop_enabled ())
+               return NULL;
+
+       ++coop_try_blocking_count;
+
        info = mono_thread_info_current_unchecked ();
        /* If the thread is not attached, it doesn't make sense prepare for suspend. */
        if (!info || !mono_thread_info_is_live (info) || mono_thread_info_current_state (info) == STATE_BLOCKING) {
@@ -164,12 +247,11 @@ mono_threads_try_prepare_blocking (void)
                return NULL;
        }
 
+       copy_stack_data (info, stackdata);
+
 retry:
-       /*The JIT might not be able to save*/
-       if (!mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX], NULL)) {
-               THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p to save thread state\n", mono_thread_info_get_tid (info));
-               return NULL;
-       }
+       ++coop_save_count;
+       mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
 
        switch (mono_threads_transition_do_blocking (info)) {
        case DoBlockingContinue:
@@ -183,84 +265,56 @@ retry:
 }
 
 void
-mono_threads_finish_try_blocking (void* cookie)
-{
-       mono_threads_finish_blocking (cookie);
-}
-
-void
-mono_threads_core_abort_syscall (MonoThreadInfo *info)
-{
-       g_error ("FIXME");
-}
-
-gboolean
-mono_threads_core_begin_async_resume (MonoThreadInfo *info)
-{
-       g_error ("FIXME");
-       return FALSE;
-}
-
-gboolean
-mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
-{
-       mono_threads_add_to_pending_operation_set (info);
-       /* There's nothing else to do after we async request the thread to suspend */
-       return TRUE;
-}
-
-gboolean
-mono_threads_core_check_suspend_result (MonoThreadInfo *info)
+mono_threads_finish_try_blocking (void* cookie, void* stackdata)
 {
-       /* Async suspend can't async fail on coop */
-       return TRUE;
-}
+       if (!mono_threads_is_coop_enabled ())
+               return;
 
-gboolean
-mono_threads_core_needs_abort_syscall (void)
-{
-       /*
-       Small digression.
-       Syscall abort can't be handled by the suspend machinery even though it's kind of implemented
-       in a similar way (with, like, signals).
-
-       So, having it here is wrong, it should be on mono-threads-(mach|posix|windows).
-       Ideally we would slice this in (coop|preemp) and target. Then have this file set:
-       mono-threads-mach, mono-threads-mach-preempt and mono-threads-mach-coop.
-       More files, less ifdef hell.
-       */
-       return FALSE;
+       mono_threads_finish_blocking (cookie, stackdata);
 }
 
 void
-mono_threads_init_platform (void)
+mono_threads_init_coop (void)
 {
+       if (!mono_threads_is_coop_enabled ())
+               return;
+
+       mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
+       mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
+       mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
+       mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
+       mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
        //See the above for what's wrong here.
 }
 
 void
-mono_threads_platform_free (MonoThreadInfo *info)
+mono_threads_coop_begin_global_suspend (void)
 {
-       //See the above for what's wrong here.
+       if (mono_threads_is_coop_enabled ())
+               mono_polling_required = 1;
 }
 
 void
-mono_threads_platform_register (MonoThreadInfo *info)
+mono_threads_coop_end_global_suspend (void)
 {
-       //See the above for what's wrong here.
+       if (mono_threads_is_coop_enabled ())
+               mono_polling_required = 0;
 }
 
-void
-mono_threads_core_begin_global_suspend (void)
+void*
+mono_threads_enter_gc_unsafe_region (void* stackdata)
 {
-       mono_polling_required = 1;
+       if (!mono_threads_is_coop_enabled ())
+               return NULL;
+
+       return mono_threads_reset_blocking_start (stackdata);
 }
 
 void
-mono_threads_core_end_global_suspend (void)
+mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)
 {
-       mono_polling_required = 0;
-}
-
+       if (!mono_threads_is_coop_enabled ())
+               return;
 
-#endif
\ No newline at end of file
+       mono_threads_reset_blocking_end (regions_cookie, stackdata);
+}