void* mono_threads_reset_blocking_start (void) { MonoThreadInfo *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; 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"); } }
void mono_threads_finish_blocking (void *cookie) { static gboolean warned_about_bad_transition; MonoThreadInfo *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_try_prepare_blocking (void) { 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) || 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; } retry: 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_state_poll (void) { MonoThreadInfo *info; ++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; } }
/* Signal that the current thread wants to be suspended. This function can be called without holding the suspend lock held. To finish suspending, call mono_suspend_check. */ void mono_thread_info_begin_self_suspend (void) { MonoThreadInfo *info = mono_thread_info_current_unchecked (); if (!info) return; THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info); mono_threads_transition_request_self_suspension (info); }
void mono_threads_reset_blocking_end (void *cookie) { MonoThreadInfo *info = cookie; if (!info) return; g_assert (info == mono_thread_info_current_unchecked ()); mono_threads_prepare_blocking (); }
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); }
DWORD mono_win32_wsa_wait_for_multiple_events (DWORD count, const WSAEVENT FAR *handles, BOOL waitAll, DWORD timeout, BOOL alertable) { DWORD result = WAIT_FAILED; MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); result = WSAWaitForMultipleEvents (count, handles, waitAll, timeout, alertable); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (info) leave_alertable_wait (info); return result; }
DWORD mono_win32_sleep_ex (DWORD timeout, BOOL alertable) { DWORD result = WAIT_FAILED; MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); result = SleepEx (timeout, alertable); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (info) leave_alertable_wait (info); return result; }
DWORD mono_win32_signal_object_and_wait (HANDLE toSignal, HANDLE toWait, DWORD timeout, BOOL alertable) { DWORD result = WAIT_FAILED; MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); result = SignalObjectAndWait (toSignal, toWait, timeout, alertable); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (info) leave_alertable_wait (info); return result; }
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; }
DWORD mono_win32_msg_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, DWORD timeout, DWORD wakeMask, DWORD flags) { DWORD result = WAIT_FAILED; BOOL alertable = flags & MWMO_ALERTABLE; MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); result = MsgWaitForMultipleObjectsEx (count, handles, timeout, wakeMask, flags); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (info) leave_alertable_wait (info); return result; }
DWORD mono_win32_wait_for_single_object_ex (HANDLE handle, DWORD timeout, BOOL alertable) { DWORD result = WAIT_FAILED; MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); result = WaitForSingleObjectEx (handle, timeout, alertable); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (alertable && info) { leave_alertable_wait (info); } return result; }
void checked_build_thread_transition (const char *transition, void *info, int from_state, int suspend_count, int next_state, int suspend_count_delta) { MonoThreadInfo *cur = mono_thread_info_current_unchecked (); CheckState *state = get_state (); /* We currently don't record external changes as those are hard to reason about. */ if (cur != info) return; if (state->transitions->len >= MAX_TRANSITIONS) free_transition (g_ptr_array_remove_index (state->transitions, 0)); ThreadTransition *t = g_new0 (ThreadTransition, 1); t->name = transition; t->from_state = from_state; t->next_state = next_state; t->suspend_count = suspend_count; t->suspend_count_delta = suspend_count_delta; t->size = collect_backtrace (t->backtrace); g_ptr_array_add (state->transitions, t); }
DWORD mono_win32_wait_for_multiple_objects_ex (DWORD count, CONST HANDLE *handles, BOOL waitAll, DWORD timeout, BOOL alertable, MonoError *error) { MonoThreadInfo * const info = alertable ? mono_thread_info_current_unchecked () : NULL; if (info) enter_alertable_wait (info); DWORD const result = WaitForMultipleObjectsEx (count, handles, waitAll, timeout, alertable); // NOTE, leave_alertable_wait should not affect GetLastError but // if changed, GetLastError needs to be preserved and reset before returning. if (info) leave_alertable_wait (info); // This is not perfect, but it is the best you can do in usermode and matches CoreCLR. // i.e. handle-based instead of object-based. if (result == WAIT_FAILED && waitAll && error && count > 1 && count <= MAXIMUM_WAIT_OBJECTS && GetLastError () == ERROR_INVALID_PARAMETER) { gpointer handles_sorted [MAXIMUM_WAIT_OBJECTS]; // 64 memcpy (handles_sorted, handles, count * sizeof (handles [0])); qsort (handles_sorted, count, sizeof (handles_sorted [0]), g_direct_equal); for (DWORD i = 1; i < count; ++i) { if (handles_sorted [i - 1] == handles_sorted [i]) { mono_error_set_duplicate_wait_object (error); mono_trace (G_LOG_LEVEL_DEBUG, MONO_TRACE_IO_LAYER_HANDLE, "%s: handle %p is duplicated", __func__, handles_sorted [i]); // Preserve LastError, but reduce triggering write breakpoints. if (GetLastError () != ERROR_INVALID_PARAMETER) SetLastError (ERROR_INVALID_PARAMETER); break; } } } return result; }
gpointer mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackdata) { return mono_threads_enter_gc_unsafe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata); }
void mono_thread_info_suspend_lock (void) { mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ()); }
void mono_threads_state_poll (void) { mono_threads_state_poll_with_info (mono_thread_info_current_unchecked ()); }