/* * 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 (); }
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 (); }
/* 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; 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); /* XXX this clears HP 1, so we restated it again */ // mono_atomic_store_release (&mono_thread_info_current ()->inside_critical_region, TRUE); mono_threads_end_global_suspend (); mono_thread_info_suspend_unlock (); 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 }