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 ()); 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: 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 (); }
static MonoThreadInfo* suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel) { MonoThreadInfo *info = NULL; int sleep_duration = 0; for (;;) { if (!(info = suspend_sync (id, interrupt_kernel))) { mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); return NULL; } /*WARNING: We now are in interrupt context until we resume the thread. */ if (!is_thread_in_critical_region (info)) break; if (!mono_thread_info_core_resume (info)) { mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); return NULL; } THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id); /* Wait for the pending resume to finish */ mono_threads_wait_pending_operations (); if (sleep_duration == 0) mono_thread_info_yield (); else g_usleep (sleep_duration); sleep_duration += 10; } return info; }
gboolean mono_thread_info_resume (MonoNativeThreadId tid) { gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */ MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); MonoThreadInfo *info; THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid); mono_thread_info_suspend_lock (); info = mono_thread_info_lookup (tid); /*info on HP1*/ if (!info) { result = FALSE; goto cleanup; } result = mono_thread_info_core_resume (info); //Wait for the pending resume to finish mono_threads_wait_pending_operations (); cleanup: mono_thread_info_suspend_unlock (); mono_hazard_pointer_clear (hp, 1); return result; }
void mono_thread_info_finish_suspend_and_resume (MonoThreadInfo *info) { MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); /*Resume can access info after the target has resumed, so we must ensure it won't touch freed memory. */ mono_hazard_pointer_set (hp, 1, info); mono_thread_info_core_resume (info); mono_hazard_pointer_clear (hp, 1); mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, FALSE); }
/* WARNING: If we are trying to suspend a target that is on a critical region and running a syscall we risk looping forever if @interrupt_kernel is FALSE. So, be VERY carefull in calling this with @interrupt_kernel == FALSE. Info is not put on a hazard pointer as a suspended thread cannot exit and be freed. This function MUST be matched with mono_thread_info_finish_suspend or mono_thread_info_finish_suspend_and_resume */ MonoThreadInfo* mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel) { MonoThreadInfo *info = NULL; int sleep_duration = 0; /*FIXME: unify this with self-suspend*/ g_assert (id != mono_native_thread_id_get ()); mono_thread_info_suspend_lock (); for (;;) { const char *suspend_error = "Unknown error"; if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) { g_warning ("failed to suspend thread %p due to %s, hopefully it is dead", (gpointer)id, suspend_error); mono_thread_info_suspend_unlock (); return NULL; } /*WARNING: We now are in interrupt context until we resume the thread. */ if (!is_thread_in_critical_region (info)) break; if (!mono_thread_info_core_resume (info)) { g_warning ("failed to resume thread %p, hopefully it is dead", (gpointer)id); mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); mono_thread_info_suspend_unlock (); return NULL; } THREADS_DEBUG ("restarted thread %p\n", (gpointer)id); if (!sleep_duration) { #ifdef HOST_WIN32 SwitchToThread (); #else sched_yield (); #endif } else { g_usleep (sleep_duration); } sleep_duration += 10; } /* XXX this clears HP 1, so we restated it again */ mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE); mono_thread_info_suspend_unlock (); return info; }
gboolean mono_thread_info_resume (MonoNativeThreadId tid) { gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */ MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/ if (!info) { result = FALSE; goto cleanup; } result = mono_thread_info_core_resume (info); cleanup: mono_hazard_pointer_clear (hp, 1); return result; }
/* The return value is only valid until a matching mono_thread_info_resume is called */ static MonoThreadInfo* suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel) { MonoThreadHazardPointers *hp = mono_hazard_pointer_get (); MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/ if (!info) return NULL; switch (mono_threads_transition_request_async_suspension (info)) { case AsyncSuspendAlreadySuspended: mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections return info; case AsyncSuspendWait: mono_threads_add_to_pending_operation_set (info); break; case AsyncSuspendInitSuspend: if (!begin_async_suspend (info, interrupt_kernel)) { mono_hazard_pointer_clear (hp, 1); return NULL; } break; case AsyncSuspendBlocking: if (interrupt_kernel) mono_threads_suspend_abort_syscall (info); break; default: g_assert_not_reached (); } //Wait for the pending suspend to finish mono_threads_wait_pending_operations (); if (!check_async_suspend (info)) { mono_thread_info_core_resume (info); mono_threads_wait_pending_operations (); mono_hazard_pointer_clear (hp, 1); return NULL; } return info; }
static MonoThreadInfo* suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel) { MonoThreadInfo *info = NULL; int sleep_duration = 0; for (;;) { const char *suspend_error = "Unknown error"; if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) { mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); return NULL; } /*WARNING: We now are in interrupt context until we resume the thread. */ if (!is_thread_in_critical_region (info)) break; if (!mono_thread_info_core_resume (info)) { mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1); return NULL; } THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id); /* Wait for the pending resume to finish */ mono_threads_wait_pending_operations (); if (!sleep_duration) { #ifdef HOST_WIN32 SwitchToThread (); #else sched_yield (); #endif } else { g_usleep (sleep_duration); } sleep_duration += 10; } return info; }
gboolean mono_thread_info_begin_resume (MonoThreadInfo *info) { return mono_thread_info_core_resume (info); }