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; }
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; }
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 (); }
/* * This is a very specific function whose only purpose is to * break a given thread from socket syscalls. * * This only exists because linux won't fail a call to connect * if the underlying is closed. * * TODO We should cleanup and unify this with the other syscall abort * facility. */ void mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid) { MonoThreadHazardPointers *hp; MonoThreadInfo *info; if (tid == mono_native_thread_id_get () || !mono_threads_core_needs_abort_syscall ()) return; hp = mono_hazard_pointer_get (); info = mono_thread_info_lookup (tid); if (!info) return; if (mono_thread_info_run_state (info) == STATE_DETACHED) { mono_hazard_pointer_clear (hp, 1); return; } mono_thread_info_suspend_lock (); mono_threads_begin_global_suspend (); mono_threads_core_abort_syscall (info); mono_threads_wait_pending_operations (); mono_hazard_pointer_clear (hp, 1); mono_threads_end_global_suspend (); mono_thread_info_suspend_unlock (); }
/* 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; } } //Wait for the pending suspend to finish mono_threads_wait_pending_operations (); if (!check_async_suspend (info)) { mono_hazard_pointer_clear (hp, 1); return NULL; } return info; }
static void sgen_unified_suspend_restart_world (void) { THREADS_STW_DEBUG ("[GC-STW-END] *** BEGIN RESUME ***\n"); FOREACH_THREAD (info) { int reason = 0; if (sgen_is_thread_in_current_stw (info, &reason)) { g_assert (mono_thread_info_begin_resume (info)); THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] RESUME thread %p\n", mono_thread_info_get_tid (info)); binary_protocol_thread_restart ((gpointer) mono_thread_info_get_tid (info)); } else { THREADS_STW_DEBUG ("[GC-STW-RESUME-WORLD] IGNORE thread %p, reason %d\n", mono_thread_info_get_tid (info), reason); } } FOREACH_THREAD_END mono_threads_wait_pending_operations (); mono_threads_end_global_suspend (); }
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; }
static void sgen_unified_suspend_stop_world (void) { int restart_counter; int sleep_duration = -1; mono_threads_begin_global_suspend (); THREADS_STW_DEBUG ("[GC-STW-BEGIN] *** BEGIN SUSPEND *** \n"); FOREACH_THREAD (info) { int reason; info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; if (sgen_is_thread_in_current_stw (info, &reason)) { info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), info->client_info.skip); } else { THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip, reason); } } FOREACH_THREAD_END mono_thread_info_current ()->client_info.suspend_done = TRUE; mono_threads_wait_pending_operations (); for (;;) { restart_counter = 0; FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } /* All threads that reach here are pristine suspended. This means the following: - We haven't accepted the previous suspend as good. - We haven't gave up on it for this STW (it's either bad or asked not to) */ if (mono_thread_info_in_critical_location (info)) { gboolean res; gint suspend_count = mono_thread_info_suspend_count (info); if (!(suspend_count == 1)) g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); res = mono_thread_info_begin_resume (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %d\n", mono_thread_info_get_tid (info), res); if (res) ++restart_counter; else info->client_info.skip = TRUE; } else { THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); g_assert (!info->client_info.in_critical_region); info->client_info.suspend_done = TRUE; } } FOREACH_THREAD_END if (restart_counter == 0) break; mono_threads_wait_pending_operations (); if (sleep_duration < 0) { mono_thread_info_yield (); sleep_duration = 0; } else { g_usleep (sleep_duration); sleep_duration += 10; } FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } if (mono_thread_info_is_running (info)) { gboolean res = mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %d\n", mono_thread_info_get_tid (info), res); if (!res) info->client_info.skip = TRUE; } } FOREACH_THREAD_END mono_threads_wait_pending_operations (); } FOREACH_THREAD (info) { int reason = 0; if (sgen_is_thread_in_current_stw (info, &reason)) { MonoThreadUnwindState *state; THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended\n", mono_thread_info_get_tid (info)); g_assert (info->client_info.suspend_done); state = mono_thread_info_get_suspend_state (info); info->client_info.ctx = state->ctx; if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN] || !state->unwind_data [MONO_UNWIND_DATA_LMF]) { /* thread is starting or detaching, nothing to scan here */ info->client_info.stopped_domain = NULL; info->client_info.stopped_ip = NULL; info->client_info.stack_start = NULL; } else { /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ info->client_info.stopped_domain = (MonoDomain*) mono_thread_info_tls_get (info, TLS_KEY_DOMAIN); info->client_info.stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */ if (!info->client_info.stack_start || info->client_info.stack_start < info->client_info.stack_start_limit || info->client_info.stack_start >= info->client_info.stack_end) { g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p", info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end); } } binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), info->client_info.stopped_ip); } else { THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); } } FOREACH_THREAD_END }
static void sgen_unified_suspend_stop_world (void) { int sleep_duration = -1; mono_threads_begin_global_suspend (); THREADS_STW_DEBUG ("[GC-STW-BEGIN][%p] *** BEGIN SUSPEND *** \n", mono_thread_info_get_tid (mono_thread_info_current ())); FOREACH_THREAD (info) { info->client_info.skip = FALSE; info->client_info.suspend_done = FALSE; int reason; if (!sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] IGNORE thread %p skip %s reason %d\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false", reason); continue; } info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-BEGIN-SUSPEND] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_thread_info_current ()->client_info.suspend_done = TRUE; mono_threads_wait_pending_operations (); for (;;) { gint restart_counter = 0; FOREACH_THREAD (info) { gint suspend_count; int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE RESUME thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } /* All threads that reach here are pristine suspended. This means the following: - We haven't accepted the previous suspend as good. - We haven't gave up on it for this STW (it's either bad or asked not to) */ if (!mono_thread_info_in_critical_location (info)) { info->client_info.suspend_done = TRUE; THREADS_STW_DEBUG ("[GC-STW-RESTART] DONE thread %p deemed fully suspended\n", mono_thread_info_get_tid (info)); continue; } suspend_count = mono_thread_info_suspend_count (info); if (!(suspend_count == 1)) g_error ("[%p] suspend_count = %d, but should be 1", mono_thread_info_get_tid (info), suspend_count); info->client_info.skip = !mono_thread_info_begin_resume (info); if (!info->client_info.skip) restart_counter += 1; THREADS_STW_DEBUG ("[GC-STW-RESTART] RESTART thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_threads_wait_pending_operations (); if (restart_counter == 0) break; if (sleep_duration < 0) { mono_thread_info_yield (); sleep_duration = 0; } else { g_usleep (sleep_duration); sleep_duration += 10; } FOREACH_THREAD (info) { int reason = 0; if (info->client_info.suspend_done || !sgen_is_thread_in_current_stw (info, &reason)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not been processed done %d current %d reason %d\n", mono_thread_info_get_tid (info), info->client_info.suspend_done, !sgen_is_thread_in_current_stw (info, NULL), reason); continue; } if (!mono_thread_info_is_running (info)) { THREADS_STW_DEBUG ("[GC-STW-RESTART] IGNORE SUSPEND thread %p not running\n", mono_thread_info_get_tid (info)); continue; } info->client_info.skip = !mono_thread_info_begin_suspend (info); THREADS_STW_DEBUG ("[GC-STW-RESTART] SUSPEND thread %p skip %s\n", mono_thread_info_get_tid (info), info->client_info.skip ? "true" : "false"); } FOREACH_THREAD_END mono_threads_wait_pending_operations (); } FOREACH_THREAD (info) { gpointer stopped_ip; int reason = 0; if (!sgen_is_thread_in_current_stw (info, &reason)) { g_assert (!info->client_info.suspend_done || info == mono_thread_info_current ()); THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is NOT suspended, reason %d\n", mono_thread_info_get_tid (info), reason); continue; } g_assert (info->client_info.suspend_done); info->client_info.ctx = mono_thread_info_get_suspend_state (info)->ctx; /* Once we remove the old suspend code, we should move sgen to directly access the state in MonoThread */ info->client_info.stack_start = (gpointer) ((char*)MONO_CONTEXT_GET_SP (&info->client_info.ctx) - REDZONE_SIZE); /* altstack signal handler, sgen can't handle them, mono-threads should have handled this. */ if (!info->client_info.stack_start || info->client_info.stack_start < info->client_info.stack_start_limit || info->client_info.stack_start >= info->client_info.stack_end) { g_error ("BAD STACK: stack_start = %p, stack_start_limit = %p, stack_end = %p", info->client_info.stack_start, info->client_info.stack_start_limit, info->client_info.stack_end); } stopped_ip = (gpointer) (MONO_CONTEXT_GET_IP (&info->client_info.ctx)); binary_protocol_thread_suspend ((gpointer) mono_thread_info_get_tid (info), stopped_ip); THREADS_STW_DEBUG ("[GC-STW-SUSPEND-END] thread %p is suspended, stopped_ip = %p, stack = %p -> %p\n", mono_thread_info_get_tid (info), stopped_ip, info->client_info.stack_start, info->client_info.stack_start ? info->client_info.stack_end : NULL); } FOREACH_THREAD_END }