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) { THREADS_SUSPEND_DEBUG ("PREPARE-TRY-BLOCKING failed %p\n", mono_thread_info_get_tid (info)); return NULL; } copy_stack_data (info, stackdata); retry: ++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: break; case DoBlockingPollAndRetry: mono_threads_state_poll (); goto retry; } return info; }
void* mono_threads_reset_blocking_start (void* stackdata) { 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; return NULL; case AbortBlockingIgnoreAndPoll: mono_threads_state_poll (); return NULL; case AbortBlockingOk: info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE; return info; case AbortBlockingOkAndPool: mono_threads_state_poll (); return info; default: g_error ("Unknown thread state"); } }
static gpointer mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata) { if (!mono_threads_is_coop_enabled ()) return NULL; ++coop_do_blocking_count; check_info (info, "enter", "safe"); copy_stack_data (info, stackdata); retry: ++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: break; case DoBlockingPollAndRetry: mono_threads_state_poll_with_info (info); goto retry; } return info; }
gpointer mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata) { if (!mono_threads_is_coop_enabled ()) return NULL; ++coop_reset_blocking_count; check_info (info, "enter", "unsafe"); copy_stack_data (info, stackdata); switch (mono_threads_transition_abort_blocking (info)) { case AbortBlockingIgnore: info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE; return NULL; case AbortBlockingIgnoreAndPoll: mono_threads_state_poll_with_info (info); return NULL; case AbortBlockingOk: info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE; break; case AbortBlockingWait: mono_thread_info_wait_for_resume (info); break; default: g_error ("Unknown thread state"); } return info; }
static void rehash (MonoGHashTable *hash) { MONO_REQ_GC_UNSAFE_MODE; //we must run in unsafe mode to make rehash safe int diff = ABS (hash->last_rehash - hash->in_use); RehashData data; void *old_table G_GNUC_UNUSED; /* unused on Boehm */ /* These are the factors to play with to change the rehashing strategy */ /* I played with them with a large range, and could not really get */ /* something that was too good, maybe the tests are not that great */ if (!(diff * 0.75 > hash->table_size * 2)) return; data.hash = hash; data.new_size = g_spaced_primes_closest (hash->in_use); data.table = mg_new0 (Slot *, data.new_size); if (!mono_threads_is_coop_enabled ()) { old_table = mono_gc_invoke_with_gc_lock (do_rehash, &data); } else { /* We cannot be preempted */ old_table = do_rehash (&data); } mg_free (old_table); }
void mono_threads_finish_blocking (void *cookie, void* stackdata) { static gboolean warned_about_bad_transition; MonoThreadInfo *info; if (!mono_threads_is_coop_enabled ()) return; info = cookie; if (!info) return; g_assert (info == mono_thread_info_current_unchecked ()); switch (mono_threads_transition_done_blocking (info)) { case DoneBlockingAborted: if (!warned_about_bad_transition) { warned_about_bad_transition = TRUE; g_warning ("[%p] Blocking call ended in running state for, this might lead to unbound GC pauses.", mono_thread_info_get_tid (info)); } mono_threads_state_poll (); break; case DoneBlockingOk: info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE; break; case DoneBlockingWait: THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n"); mono_thread_info_wait_for_resume (info); break; default: g_error ("Unknown thread state"); } }
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; THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info)); /* Fast check for pending suspend requests */ if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED))) return; ++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)) { case SelfSuspendResumed: return; case SelfSuspendWait: mono_thread_info_wait_for_resume (info); break; case SelfSuspendNotifyAndWait: mono_threads_notify_initiator_of_suspend (info); mono_thread_info_wait_for_resume (info); break; } }
void mono_threads_finish_try_blocking (void* cookie, void* stackdata) { if (!mono_threads_is_coop_enabled ()) return; mono_threads_finish_blocking (cookie, stackdata); }
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); }
static void resume_async_suspended (MonoThreadInfo *info) { if (mono_threads_is_coop_enabled ()) g_assert_not_reached (); g_assert (mono_threads_core_begin_async_resume (info)); }
static gboolean check_async_suspend (MonoThreadInfo *info) { if (mono_threads_is_coop_enabled ()) { /* Async suspend can't async fail on coop */ return TRUE; } return mono_threads_core_check_suspend_result (info); }
void mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackdata) { if (!mono_threads_is_coop_enabled ()) return; if (!cookie) return; mono_threads_enter_gc_safe_region_unbalanced (stackdata); }
static gboolean begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel) { if (mono_threads_is_coop_enabled ()) { /* There's nothing else to do after we async request the thread to suspend */ mono_threads_add_to_pending_operation_set (info); return TRUE; } return mono_threads_core_begin_async_suspend (info, interrupt_kernel); }
void mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) { gboolean res; threads_callbacks = *callbacks; thread_info_size = info_size; const char *sleepLimit; #ifdef HOST_WIN32 res = mono_native_tls_alloc (&thread_info_key, NULL); res = mono_native_tls_alloc (&thread_exited_key, NULL); #else res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor); res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor); #endif g_assert (res); #ifndef HAVE_KW_THREAD res = mono_native_tls_alloc (&small_id_key, NULL); #endif g_assert (res); unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled (); if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) { errno = 0; long threshold = strtol(sleepLimit, NULL, 10); if ((errno == 0) && (threshold >= 40)) { sleepAbortDuration = threshold; sleepWarnDuration = threshold / 20; } else g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40"); } mono_os_sem_init (&global_suspend_semaphore, 1); mono_os_sem_init (&suspend_semaphore, 0); mono_lls_init (&thread_list, NULL, HAZARD_FREE_NO_LOCK); mono_thread_smr_init (); mono_threads_platform_init (); mono_threads_suspend_init (); mono_threads_coop_init (); mono_threads_abort_syscall_init (); #if defined(__MACH__) mono_mach_init (thread_info_key); #endif mono_threads_inited = TRUE; g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t)); }
void mono_threads_exit_gc_unsafe_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_unsafe_region_unbalanced (cookie, stackdata); }
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_reset_blocking_end (void *cookie, void* stackdata) { 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 (stackdata); }
gpointer mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, gpointer *stackdata) { gpointer cookie; if (!mono_threads_is_coop_enabled ()) return NULL; cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata); #ifdef ENABLE_CHECKED_BUILD_GC if (mono_check_mode_enabled (MONO_CHECK_MODE_GC)) coop_tls_push (cookie); #endif return cookie; }
void mono_threads_coop_init (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. #ifdef ENABLE_CHECKED_BUILD_GC mono_native_tls_alloc (&coop_reset_count_stack_key, NULL); #endif }
gpointer mono_threads_enter_gc_unsafe_region_cookie (void) { MonoThreadInfo *info; g_assert (mono_threads_is_coop_enabled ()); info = mono_thread_info_current_unchecked (); check_info (info, "enter (cookie)", "unsafe"); #ifdef ENABLE_CHECKED_BUILD_GC if (mono_check_mode_enabled (MONO_CHECK_MODE_GC)) coop_tls_push (info); #endif return info; }
void mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size) { gboolean res; threads_callbacks = *callbacks; thread_info_size = info_size; #ifdef HOST_WIN32 res = mono_native_tls_alloc (&thread_info_key, NULL); res = mono_native_tls_alloc (&thread_exited_key, NULL); #else res = mono_native_tls_alloc (&thread_info_key, (void *) unregister_thread); res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor); #endif g_assert (res); #ifndef HAVE_KW_THREAD res = mono_native_tls_alloc (&small_id_key, NULL); #endif g_assert (res); unified_suspend_enabled = g_getenv ("MONO_ENABLE_UNIFIED_SUSPEND") != NULL || mono_threads_is_coop_enabled (); mono_coop_sem_init (&global_suspend_semaphore, 1); mono_os_sem_init (&suspend_semaphore, 0); mono_lls_init (&thread_list, NULL); mono_thread_smr_init (); mono_threads_init_platform (); mono_threads_init_coop (); mono_threads_init_abort_syscall (); #if defined(__MACH__) mono_mach_init (thread_info_key); #endif mono_threads_inited = TRUE; g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t)); }
void mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data) { int result; MonoThreadInfo *info = NULL; MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id); /*FIXME: unify this with self-suspend*/ g_assert (id != mono_native_thread_id_get ()); /* This can block during stw */ mono_thread_info_suspend_lock (); mono_threads_begin_global_suspend (); info = suspend_sync_nolock (id, interrupt_kernel); if (!info) goto done; switch (result = callback (info, user_data)) { case MonoResumeThread: mono_hazard_pointer_set (hp, 1, info); mono_thread_info_core_resume (info); mono_threads_wait_pending_operations (); break; case KeepSuspended: g_assert (!mono_threads_is_coop_enabled ()); break; default: g_error ("Invalid suspend_and_run callback return value %d", result); } done: mono_hazard_pointer_clear (hp, 1); mono_threads_end_global_suspend (); mono_thread_info_suspend_unlock (); }
void mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackdata) { MonoThreadInfo *info; if (!mono_threads_is_coop_enabled ()) return; info = (MonoThreadInfo *)cookie; check_info (info, "exit", "safe"); switch (mono_threads_transition_done_blocking (info)) { case DoneBlockingOk: info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE; break; case DoneBlockingWait: THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n"); mono_thread_info_wait_for_resume (info); break; default: g_error ("Unknown thread state"); } }
void mono_threads_coop_end_global_suspend (void) { if (mono_threads_is_coop_enabled ()) mono_polling_required = 0; }