Esempio n. 1
0
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;
}
Esempio n. 2
0
File: trace.c Progetto: RavenB/mono
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 {
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
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;
}
Esempio n. 8
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
}
Esempio n. 9
0
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;
}
Esempio n. 10
0
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
}