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_trace_enter_method (MonoMethod *method, char *ebp) { int i, j; MonoClass *klass; MonoObject *o; MonoJitArgumentInfo *arg_info; MonoMethodSignature *sig; char *fname; MonoGenericSharingContext *gsctx = NULL; if (!trace_spec.enabled) return; while (output_lock != 0 || InterlockedCompareExchange (&output_lock, 1, 0) != 0) mono_thread_info_yield (); fname = mono_method_full_name (method, TRUE); indent (1); printf ("ENTER: %s(", fname); g_free (fname); if (!ebp) { printf (") ip: %p\n", RETURN_ADDRESS_N (1)); goto unlock; } sig = mono_method_signature (method); arg_info = alloca (sizeof (MonoJitArgumentInfo) * (sig->param_count + 1)); if (method->is_inflated) { /* FIXME: Might be better to pass the ji itself */ MonoJitInfo *ji = mini_jit_info_table_find (mono_domain_get (), RETURN_ADDRESS (), NULL); if (ji) { gsctx = mono_jit_info_get_generic_sharing_context (ji); if (gsctx && gsctx->is_gsharedvt) { /* Needs a ctx to get precise method */ printf (") <gsharedvt>\n"); goto unlock; } } } mono_arch_get_argument_info (sig, sig->param_count, arg_info); if (MONO_TYPE_ISSTRUCT (mono_method_signature (method)->ret)) { g_assert (!mono_method_signature (method)->ret->byref); printf ("VALUERET:%p, ", *((gpointer *)(ebp + 8))); } if (mono_method_signature (method)->hasthis) { gpointer *this = (gpointer *)(ebp + arg_info [0].offset); if (method->klass->valuetype) { printf ("value:%p, ", *arg_in_stack_slot(this, gpointer *)); } else {
gboolean mono_rand_open (void) { static gint32 status = 0; if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) { while (status != 2) mono_thread_info_yield (); return TRUE; } srand (time (NULL)); status = 2; return TRUE; }
gboolean mono_rand_open (void) { static gint32 status = 0; if (status != 0 || InterlockedCompareExchange (&status, 1, 0) != 0) { while (status != 2) mono_thread_info_yield (); return TRUE; } #ifdef NAME_DEV_URANDOM file = open (NAME_DEV_URANDOM, O_RDONLY); #endif #ifdef NAME_DEV_RANDOM if (file < 0) file = open (NAME_DEV_RANDOM, O_RDONLY); #endif if (file < 0) use_egd = g_getenv("MONO_EGD_SOCKET") != NULL; status = 2; return TRUE; }
static void ensure_cleanedup (void) { if (status == STATUS_NOT_INITIALIZED && InterlockedCompareExchange (&status, STATUS_CLEANED_UP, STATUS_NOT_INITIALIZED) == STATUS_NOT_INITIALIZED) return; if (status == STATUS_INITIALIZING) { while (status == STATUS_INITIALIZING) mono_thread_info_yield (); } if (status == STATUS_CLEANED_UP) return; if (status == STATUS_CLEANING_UP || InterlockedCompareExchange (&status, STATUS_CLEANING_UP, STATUS_INITIALIZED) != STATUS_INITIALIZED) { while (status == STATUS_CLEANING_UP) mono_thread_info_yield (); g_assert (status == STATUS_CLEANED_UP); return; } /* we make the assumption along the code that we are * cleaning up only if the runtime is shutting down */ g_assert (mono_runtime_is_shutting_down ()); /* Unpark all worker threads */ mono_mutex_lock (&threadpool->parked_threads_lock); for (;;) { guint i; ThreadPoolCounter counter = COUNTER_READ (); if (counter._.active == 0 && counter._.parked == 0) break; if (counter._.active == 1) { MonoInternalThread *thread = mono_thread_internal_current (); if (thread->threadpool_thread) { /* if there is only one active thread * left and it's the current one */ break; } } for (i = 0; i < threadpool->parked_threads->len; ++i) { mono_cond_t *cond = (mono_cond_t*) g_ptr_array_index (threadpool->parked_threads, i); mono_cond_signal (cond); } mono_mutex_unlock (&threadpool->parked_threads_lock); usleep (1000); mono_mutex_lock (&threadpool->parked_threads_lock); } mono_mutex_unlock (&threadpool->parked_threads_lock); while (monitor_status != MONITOR_STATUS_NOT_RUNNING) usleep (1000); g_ptr_array_free (threadpool->domains, TRUE); mono_mutex_destroy (&threadpool->domains_lock); g_ptr_array_free (threadpool->parked_threads, TRUE); mono_mutex_destroy (&threadpool->parked_threads_lock); g_ptr_array_free (threadpool->working_threads, TRUE); mono_mutex_destroy (&threadpool->working_threads_lock); mono_mutex_destroy (&threadpool->heuristic_lock); g_free (threadpool->heuristic_hill_climbing.samples); g_free (threadpool->heuristic_hill_climbing.thread_counts); rand_free (threadpool->heuristic_hill_climbing.random_interval_generator); g_free (threadpool->cpu_usage_state); g_assert (threadpool); g_free (threadpool); threadpool = NULL; g_assert (!threadpool); status = STATUS_CLEANED_UP; }
static void ensure_initialized (gboolean *enable_worker_tracking) { ThreadPoolHillClimbing *hc; const char *threads_per_cpu_env; gint threads_per_cpu; gint threads_count; if (enable_worker_tracking) { // TODO implement some kind of switch to have the possibily to use it *enable_worker_tracking = FALSE; } if (status >= STATUS_INITIALIZED) return; if (status == STATUS_INITIALIZING || InterlockedCompareExchange (&status, STATUS_INITIALIZING, STATUS_NOT_INITIALIZED) != STATUS_NOT_INITIALIZED) { while (status == STATUS_INITIALIZING) mono_thread_info_yield (); g_assert (status >= STATUS_INITIALIZED); return; } g_assert (!threadpool); threadpool = g_new0 (ThreadPool, 1); g_assert (threadpool); threadpool->domains = g_ptr_array_new (); mono_mutex_init_recursive (&threadpool->domains_lock); threadpool->parked_threads = g_ptr_array_new (); mono_mutex_init (&threadpool->parked_threads_lock); threadpool->working_threads = g_ptr_array_new (); mono_mutex_init (&threadpool->working_threads_lock); threadpool->heuristic_adjustment_interval = 10; mono_mutex_init (&threadpool->heuristic_lock); mono_rand_open (); hc = &threadpool->heuristic_hill_climbing; hc->wave_period = HILL_CLIMBING_WAVE_PERIOD; hc->max_thread_wave_magnitude = HILL_CLIMBING_MAX_WAVE_MAGNITUDE; hc->thread_magnitude_multiplier = (gdouble) HILL_CLIMBING_WAVE_MAGNITUDE_MULTIPLIER; hc->samples_to_measure = hc->wave_period * HILL_CLIMBING_WAVE_HISTORY_SIZE; hc->target_throughput_ratio = (gdouble) HILL_CLIMBING_BIAS; hc->target_signal_to_noise_ratio = (gdouble) HILL_CLIMBING_TARGET_SIGNAL_TO_NOISE_RATIO; hc->max_change_per_second = (gdouble) HILL_CLIMBING_MAX_CHANGE_PER_SECOND; hc->max_change_per_sample = (gdouble) HILL_CLIMBING_MAX_CHANGE_PER_SAMPLE; hc->sample_interval_low = HILL_CLIMBING_SAMPLE_INTERVAL_LOW; hc->sample_interval_high = HILL_CLIMBING_SAMPLE_INTERVAL_HIGH; hc->throughput_error_smoothing_factor = (gdouble) HILL_CLIMBING_ERROR_SMOOTHING_FACTOR; hc->gain_exponent = (gdouble) HILL_CLIMBING_GAIN_EXPONENT; hc->max_sample_error = (gdouble) HILL_CLIMBING_MAX_SAMPLE_ERROR_PERCENT; hc->current_control_setting = 0; hc->total_samples = 0; hc->last_thread_count = 0; hc->average_throughput_noise = 0; hc->elapsed_since_last_change = 0; hc->accumulated_completion_count = 0; hc->accumulated_sample_duration = 0; hc->samples = g_new0 (gdouble, hc->samples_to_measure); hc->thread_counts = g_new0 (gdouble, hc->samples_to_measure); hc->random_interval_generator = rand_create (); hc->current_sample_interval = rand_next (&hc->random_interval_generator, hc->sample_interval_low, hc->sample_interval_high); if (!(threads_per_cpu_env = g_getenv ("MONO_THREADS_PER_CPU"))) threads_per_cpu = 1; else threads_per_cpu = CLAMP (atoi (threads_per_cpu_env), 1, 50); threads_count = mono_cpu_count () * threads_per_cpu; threadpool->limit_worker_min = threadpool->limit_io_min = threads_count; threadpool->limit_worker_max = threadpool->limit_io_max = threads_count * 100; threadpool->counters._.max_working = threadpool->limit_worker_min; threadpool->cpu_usage_state = g_new0 (MonoCpuUsageState, 1); threadpool->suspended = FALSE; status = STATUS_INITIALIZED; }
gint mono_thread_info_sleep (guint32 ms, gboolean *alerted) { if (ms == 0) { MonoThreadInfo *info; mono_thread_info_yield (); info = mono_thread_info_current (); if (info && mono_thread_info_is_interrupt_state (info)) return WAIT_IO_COMPLETION; return 0; } if (alerted) return sleep_interruptable (ms, alerted); MONO_PREPARE_BLOCKING; if (ms == INFINITE) { do { #ifdef HOST_WIN32 Sleep (G_MAXUINT32); #else sleep (G_MAXUINT32); #endif } while (1); } else { int ret; #if defined (__linux__) && !defined(PLATFORM_ANDROID) struct timespec start, target; /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */ ret = clock_gettime (CLOCK_MONOTONIC, &start); g_assert (ret == 0); target = start; target.tv_sec += ms / 1000; target.tv_nsec += (ms % 1000) * 1000000; if (target.tv_nsec > 999999999) { target.tv_nsec -= 999999999; target.tv_sec ++; } do { ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL); } while (ret != 0); #elif HOST_WIN32 Sleep (ms); #else struct timespec req, rem; req.tv_sec = ms / 1000; req.tv_nsec = (ms % 1000) * 1000000; do { memset (&rem, 0, sizeof (rem)); ret = nanosleep (&req, &rem); } while (ret != 0); #endif /* __linux__ */ } MONO_FINISH_BLOCKING; return 0; }
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 int restart_threads_until_none_in_managed_allocator (void) { int num_threads_died = 0; int sleep_duration = -1; for (;;) { int restart_count = 0, restarted_count = 0; /* restart all threads that stopped in the allocator */ FOREACH_THREAD (info) { gboolean result; if (info->client_info.skip || info->client_info.gc_disabled || info->client_info.suspend_done) continue; if (mono_thread_info_is_live (info) && (!info->client_info.stack_start || info->client_info.in_critical_region || info->client_info.info.inside_critical_region || is_ip_in_managed_allocator (info->client_info.stopped_domain, info->client_info.stopped_ip))) { binary_protocol_thread_restart ((gpointer)mono_thread_info_get_tid (info)); SGEN_LOG (3, "thread %p resumed.", (void*) (size_t) info->client_info.info.native_handle); result = sgen_resume_thread (info); if (result) { ++restart_count; } else { info->client_info.skip = 1; } } else { /* we set the stopped_ip to NULL for threads which we're not restarting so that we can easily identify the others */ info->client_info.stopped_ip = NULL; info->client_info.stopped_domain = NULL; info->client_info.suspend_done = TRUE; } } FOREACH_THREAD_END /* if no threads were restarted, we're done */ if (restart_count == 0) break; /* wait for the threads to signal their restart */ sgen_wait_for_suspend_ack (restart_count); if (sleep_duration < 0) { mono_thread_info_yield (); sleep_duration = 0; } else { g_usleep (sleep_duration); sleep_duration += 10; } /* stop them again */ FOREACH_THREAD (info) { gboolean result; if (info->client_info.skip || info->client_info.stopped_ip == NULL) continue; result = sgen_suspend_thread (info); if (result) { ++restarted_count; } else { info->client_info.skip = 1; } } FOREACH_THREAD_END /* some threads might have died */ num_threads_died += restart_count - restarted_count; /* wait for the threads to signal their suspension again */ sgen_wait_for_suspend_ack (restarted_count); } return num_threads_died; }
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 }