* Rodrigo Kumpera (kumpera@gmail.com)
*
* Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
#include <config.h>
#include <mono/utils/mono-time.h>
#include <mono/utils/mono-counters.h>
#include <mono/utils/mono-threads-coop.h>
+#include <mono/utils/mono-threads-api.h>
+#include <mono/utils/checked-build.h>
#ifdef TARGET_OSX
#include <mono/utils/mach-support.h>
volatile size_t mono_polling_required;
+// FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
+// perhaps with a per-thread cookie in the high bits.
+#ifdef ENABLE_CHECKED_BUILD_GC
+
+// Maintains a single per-thread stack of ints, used to ensure nesting is not violated
+static MonoNativeTlsKey coop_reset_count_stack_key;
+
+static void
+coop_tls_push (gpointer cookie)
+{
+ GArray *stack;
+
+ stack = mono_native_tls_get_value (coop_reset_count_stack_key);
+ if (!stack) {
+ stack = g_array_new (FALSE, FALSE, sizeof(gpointer));
+ mono_native_tls_set_value (coop_reset_count_stack_key, stack);
+ }
+
+ g_array_append_val (stack, cookie);
+}
+
+static void
+coop_tls_pop (gpointer received_cookie)
+{
+ GArray *stack;
+ gpointer expected_cookie;
+
+ stack = mono_native_tls_get_value (coop_reset_count_stack_key);
+ if (!stack || 0 == stack->len)
+ mono_fatal_with_history ("Received cookie %p but found no stack at all, %x\n", received_cookie);
+
+ expected_cookie = g_array_index (stack, gpointer, stack->len - 1);
+ stack->len --;
+
+ if (0 == stack->len) {
+ g_array_free (stack,TRUE);
+ mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
+ }
+
+ if (expected_cookie != received_cookie)
+ mono_fatal_with_history ("Received cookie %p but expected %p\n", received_cookie, expected_cookie);
+}
+
+#endif
+
static int coop_reset_blocking_count;
static int coop_try_blocking_count;
static int coop_do_blocking_count;
}
static void
-copy_stack_data (MonoThreadInfo *info, void* stackdata_begin)
+copy_stack_data (MonoThreadInfo *info, gpointer *stackdata_begin)
{
MonoThreadUnwindState *state;
int stackdata_size;
state->gc_stackdata_size = stackdata_size;
}
-void*
-mono_threads_prepare_blocking (void* stackdata)
+gpointer
+mono_threads_enter_gc_safe_region (gpointer *stackdata)
+{
+ gpointer cookie;
+
+ if (!mono_threads_is_coop_enabled ())
+ return NULL;
+
+ cookie = mono_threads_enter_gc_safe_region_unbalanced (stackdata);
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ coop_tls_push (cookie);
+#endif
+
+ return cookie;
+}
+
+gpointer
+mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackdata)
{
MonoThreadInfo *info;
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)) {
- THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", mono_thread_info_get_tid (info));
+ THREADS_SUSPEND_DEBUG ("PREPARE-BLOCKING failed %p\n", info ? mono_thread_info_get_tid (info) : NULL);
return NULL;
}
}
void
-mono_threads_finish_blocking (void *cookie, void* stackdata)
+mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackdata)
+{
+ if (!mono_threads_is_coop_enabled ())
+ return;
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ coop_tls_pop (cookie);
+#endif
+
+ mono_threads_exit_gc_safe_region_unbalanced (cookie, stackdata);
+}
+
+void
+mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackdata)
{
static gboolean warned_about_bad_transition;
MonoThreadInfo *info;
}
}
+void
+mono_threads_assert_gc_safe_region (void)
+{
+ MONO_REQ_GC_SAFE_MODE;
+}
+
+gpointer
+mono_threads_enter_gc_unsafe_region (gpointer *stackdata)
+{
+ gpointer cookie;
+
+ if (!mono_threads_is_coop_enabled ())
+ return NULL;
-void*
-mono_threads_reset_blocking_start (void* stackdata)
+ cookie = mono_threads_enter_gc_unsafe_region_unbalanced (stackdata);
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ coop_tls_push (cookie);
+#endif
+
+ return cookie;
+}
+
+gpointer
+mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackdata)
{
MonoThreadInfo *info;
++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;
return NULL;
case AbortBlockingOk:
info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
- return info;
- case AbortBlockingOkAndPool:
- mono_threads_state_poll ();
- return info;
+ break;
+ case AbortBlockingWait:
+ mono_thread_info_wait_for_resume (info);
+ break;
default:
g_error ("Unknown thread state");
}
+
+ return info;
+}
+
+gpointer
+mono_threads_enter_gc_unsafe_region_cookie (MonoThreadInfo *info)
+{
+ g_assert (mono_threads_is_coop_enabled ());
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ coop_tls_push (info);
+#endif
+
+ return info;
}
void
-mono_threads_reset_blocking_end (void *cookie, void* stackdata)
+mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackdata)
{
- MonoThreadInfo *info;
+ if (!mono_threads_is_coop_enabled ())
+ return;
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+ coop_tls_pop (cookie);
+#endif
+
+ mono_threads_exit_gc_unsafe_region_unbalanced (cookie, stackdata);
+}
+void
+mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackdata)
+{
if (!mono_threads_is_coop_enabled ())
return;
- info = (MonoThreadInfo *)cookie;
- if (!info)
+ if (!cookie)
return;
- g_assert (info == mono_thread_info_current_unchecked ());
- mono_threads_prepare_blocking (stackdata);
+#ifdef ENABLE_CHECKED_BUILD_GC
+ if (!mono_check_mode_enabled (MONO_CHECK_MODE_GC))
+#endif
+ {
+ g_assert (((MonoThreadInfo *)cookie) == mono_thread_info_current_unchecked ());
+ }
+
+ mono_threads_enter_gc_safe_region_unbalanced (stackdata);
}
+void
+mono_threads_assert_gc_unsafe_region (void)
+{
+ MONO_REQ_GC_UNSAFE_MODE;
+}
+
+gboolean
+mono_threads_is_coop_enabled (void)
+{
+#if defined(USE_COOP_GC)
+ return TRUE;
+#else
+ static int is_coop_enabled = -1;
+ if (G_UNLIKELY (is_coop_enabled == -1))
+ is_coop_enabled = g_getenv ("MONO_ENABLE_COOP") != NULL ? 1 : 0;
+ return is_coop_enabled == 1;
+#endif
+}
+
+
void
mono_threads_init_coop (void)
{
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.
+
+#ifdef ENABLE_CHECKED_BUILD_GC
+ mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
+#endif
}
void
if (mono_threads_is_coop_enabled ())
mono_polling_required = 0;
}
-
-void*
-mono_threads_enter_gc_unsafe_region (void* stackdata)
-{
- if (!mono_threads_is_coop_enabled ())
- return NULL;
-
- return mono_threads_reset_blocking_start (stackdata);
-}
-
-void
-mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)
-{
- if (!mono_threads_is_coop_enabled ())
- return;
-
- mono_threads_reset_blocking_end (regions_cookie, stackdata);
-}