Example #1
0
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 ();
}
Example #2
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;
}
Example #3
0
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;
}
Example #4
0
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);
}
Example #5
0
/*
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;
}
Example #6
0
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;
}
Example #7
0
/*
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;
}
Example #8
0
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;
}
Example #9
0
gboolean
mono_thread_info_begin_resume (MonoThreadInfo *info)
{
	return mono_thread_info_core_resume (info);
}