示例#1
0
void
mono_threads_end_global_suspend (void) 
{
	g_assert (pending_suspends == 0);
	THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
		abort_posts, waits_done, pending_ops);
	mono_threads_core_end_global_suspend ();
}
示例#2
0
void
mono_thread_info_wait_for_resume (MonoThreadInfo* info)
{
	int res;
	THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
	res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
	g_assert (res != -1);
}
示例#3
0
void
mono_threads_begin_global_suspend (void)
{
    g_assert (pending_suspends == 0);
    THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
                           abort_posts, waits_done, pending_ops);
    g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
    mono_threads_core_begin_global_suspend ();
}
gboolean
mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
	DWORD id = mono_thread_info_get_tid (info);
	HANDLE handle;
	DWORD result;
	gboolean res;

	handle = OpenThread (THREAD_ALL_ACCESS, FALSE, id);
	g_assert (handle);

	result = SuspendThread (handle);
	THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (void*)id, ret);
	if (result == (DWORD)-1) {
		CloseHandle (handle);
		return FALSE;
	}

	/* We're in the middle of a self-suspend, resume and register */
	if (!mono_threads_transition_finish_async_suspend (info)) {
		mono_threads_add_to_pending_operation_set (info);
		result = ResumeThread (handle);
		g_assert (result == 1);
		CloseHandle (handle);
		THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/1 %p -> %d\n", (void*)id, 0);
		//XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
		return TRUE;
	}
	res = mono_threads_get_runtime_callbacks ()->thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info);
	THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (void*)id, res);
	if (res) {
		//FIXME do we need to QueueUserAPC on this case?
		if (interrupt_kernel)
			QueueUserAPC ((PAPCFUNC)interrupt_apc, handle, (ULONG_PTR)NULL);
	} else {
		mono_threads_transition_async_suspend_compensation (info);
		result = ResumeThread (handle);
		g_assert (result == 1);
		THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (void*)info->native_handle, 0);
	}

	CloseHandle (handle);
	return res;
}
示例#5
0
/*
Signal that the current thread wants to be suspended.
This function can be called without holding the suspend lock held.
To finish suspending, call mono_suspend_check.
*/
void
mono_thread_info_begin_self_suspend (void)
{
	MonoThreadInfo *info = mono_thread_info_current_unchecked ();
	if (!info)
		return;

	THREADS_SUSPEND_DEBUG ("BEGIN SELF SUSPEND OF %p\n", info);
	mono_threads_transition_request_self_suspension (info);
}
示例#6
0
gboolean
mono_threads_suspend_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
	kern_return_t ret;

	g_assert (info);


	do {
		ret = thread_suspend (info->native_handle);
	} while (ret == KERN_ABORTED);

	THREADS_SUSPEND_DEBUG ("SUSPEND %p -> %d\n", (gpointer)(gsize)info->native_handle, ret);
	if (ret != KERN_SUCCESS)
		return FALSE;

	if (!mono_threads_transition_finish_async_suspend (info)) {
		/* We raced with self-suspend and lost.  Resume the native
		 * thread.  It is still self-suspended, waiting to be resumed.
		 * So suspend can continue.
		 */
		do {
			ret = thread_resume (info->native_handle);
		} while (ret == KERN_ABORTED);
		g_assert (ret == KERN_SUCCESS);
		info->suspend_can_continue = TRUE;
		THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", (gpointer)(gsize)info->native_handle);
		g_assert (mono_threads_is_hybrid_suspension_enabled ());
		//XXX interrupt_kernel doesn't make sense in this case as the target is not in a syscall
		return TRUE;
	}
	info->suspend_can_continue = mono_threads_get_runtime_callbacks ()->
		thread_state_init_from_handle (&info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], info, NULL);
	THREADS_SUSPEND_DEBUG ("thread state %p -> %d\n", (gpointer)(gsize)info->native_handle, ret);
	if (info->suspend_can_continue) {
		if (interrupt_kernel)
			thread_abort (info->native_handle);
	} else {
		THREADS_SUSPEND_DEBUG ("FAILSAFE RESUME/2 %p -> %d\n", (gpointer)(gsize)info->native_handle, 0);
	}
	return TRUE;
}
示例#7
0
void
mono_threads_end_global_suspend (void) 
{
	size_t ps = pending_suspends;
	if (G_UNLIKELY (ps != 0))
		g_error ("pending_suspends = %d, but must be 0", ps);
	THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
		abort_posts, waits_done, pending_ops);
	g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
	mono_threads_coop_end_global_suspend ();
}
示例#8
0
gboolean
mono_threads_suspend_begin_async_resume (MonoThreadInfo *info)
{
	kern_return_t ret;

	if (info->async_target) {
		MonoContext tmp = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
		mach_msg_type_number_t num_state, num_fpstate;
		thread_state_t state, fpstate;
		ucontext_t uctx;
		mcontext_t mctx;

		mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, info->async_target, info->user_data);
		info->user_data = NULL;
		info->async_target = (void (*)(void *)) info->user_data;

		state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ());
		fpstate = (thread_state_t) alloca (mono_mach_arch_get_thread_fpstate_size ());
		mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ());

		do {
			ret = mono_mach_arch_get_thread_states (info->native_handle, state, &num_state, fpstate, &num_fpstate);
		} while (ret == KERN_ABORTED);

		if (ret != KERN_SUCCESS)
			return FALSE;

		mono_mach_arch_thread_states_to_mcontext (state, fpstate, mctx);
		uctx.uc_mcontext = mctx;
		mono_monoctx_to_sigctx (&tmp, &uctx);

		mono_mach_arch_mcontext_to_thread_states (mctx, state, fpstate);

		do {
			ret = mono_mach_arch_set_thread_states (info->native_handle, state, num_state, fpstate, num_fpstate);
		} while (ret == KERN_ABORTED);

		if (ret != KERN_SUCCESS)
			return FALSE;
	}

	do {
		ret = thread_resume (info->native_handle);
	} while (ret == KERN_ABORTED);
	THREADS_SUSPEND_DEBUG ("RESUME %p -> %d\n", (gpointer)(gsize)info->native_handle, ret);

	return ret == KERN_SUCCESS;
}
示例#9
0
gboolean
mono_threads_core_begin_async_resume (MonoThreadInfo *info)
{
	kern_return_t ret;

	if (info->async_target) {
		MonoContext tmp = info->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
		mach_msg_type_number_t num_state;
		thread_state_t state;
		ucontext_t uctx;
		mcontext_t mctx;

		mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, info->async_target, info->user_data);
		info->user_data = NULL;
		info->async_target = (void (*)(void *)) info->user_data;

		state = (thread_state_t) alloca (mono_mach_arch_get_thread_state_size ());
		mctx = (mcontext_t) alloca (mono_mach_arch_get_mcontext_size ());

		ret = mono_mach_arch_get_thread_state (info->native_handle, state, &num_state);
		if (ret != KERN_SUCCESS)
			return FALSE;

		mono_mach_arch_thread_state_to_mcontext (state, mctx);
#ifdef TARGET_ARM64
		g_assert_not_reached ();
#else
		uctx.uc_mcontext = mctx;
#endif
		mono_monoctx_to_sigctx (&tmp, &uctx);

		mono_mach_arch_mcontext_to_thread_state (mctx, state);

		ret = mono_mach_arch_set_thread_state (info->native_handle, state, num_state);
		if (ret != KERN_SUCCESS)
			return FALSE;
	}

	ret = thread_resume (info->native_handle);
	THREADS_SUSPEND_DEBUG ("RESUME %p -> %d\n", (void*)info->native_handle, ret);

	return ret == KERN_SUCCESS;
}
示例#10
0
int
mono_threads_pthread_kill (MonoThreadInfo *info, int signum)
{
	THREADS_SUSPEND_DEBUG ("sending signal %d to %p[%p]\n", signum, info, mono_thread_info_get_tid (info));
#ifdef USE_TKILL_ON_ANDROID
	int result, old_errno = errno;
	result = tkill (info->native_handle, signum);
	if (result < 0) {
		result = errno;
		errno = old_errno;
	}
	return result;
#elif defined(__native_client__)
	/* Workaround pthread_kill abort() in NaCl glibc. */
	return 0;
#else
	return pthread_kill (mono_thread_info_get_tid (info), signum);
#endif
}
示例#11
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;

	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;
}
示例#12
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;
}
示例#13
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 ());

	/* This can block during stw */
	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:
		g_assert (!mono_threads_is_coop_enabled ());
		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 ();
}
示例#14
0
void
mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackdata)
{
	MonoThreadInfo *info;

	if (!mono_threads_is_coop_enabled ())
		return;

	info = (MonoThreadInfo *)cookie;

	check_info (info, "exit", "safe");

	switch (mono_threads_transition_done_blocking (info)) {
	case DoneBlockingOk:
		info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
		break;
	case DoneBlockingWait:
		THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
		mono_thread_info_wait_for_resume (info);
		break;
	default:
		g_error ("Unknown thread state");
	}
}
示例#15
0
static void
suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
{
	int old_errno = errno;
	int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();

	MonoThreadInfo *current = mono_thread_info_current ();

	THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", mono_thread_info_get_tid (current), (void*)current->native_handle);
	if (current->syscall_break_signal) {
		current->syscall_break_signal = FALSE;
		THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", mono_thread_info_get_tid (current));
		mono_threads_notify_initiator_of_abort (current);
		goto done;
	}

	/* Have we raced with self suspend? */
	if (!mono_threads_transition_finish_async_suspend (current)) {
		current->suspend_can_continue = TRUE;
		THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", mono_thread_info_get_tid (current));
		goto done;
	}

	/*
	 * If the thread is starting, then thread_state_init_from_sigctx returns FALSE,
	 * as the thread might have been attached without the domain or lmf having been
	 * initialized yet.
	 *
	 * One way to fix that is to keep the thread suspended (wait for the restart
	 * signal), and make sgen aware that even if a thread might be suspended, there
	 * would be cases where you cannot scan its stack/registers. That would in fact
	 * consist in removing the async suspend compensation, and treat the case directly
	 * in sgen. That's also how it was done in the sgen specific suspend code.
	 */

	/* thread_state_init_from_sigctx return FALSE if the current thread is starting or detaching and suspend can't continue. */
	current->suspend_can_continue = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);

	if (!current->suspend_can_continue)
		THREADS_SUSPEND_DEBUG ("\tThread is starting or detaching, failed to capture state %p\n", mono_thread_info_get_tid (current));

	/*
	Block the restart signal.
	We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
	which might miss the signal and get stuck.
	*/
	pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);

	/* We're done suspending */
	mono_threads_notify_initiator_of_suspend (current);

	do {
		current->signal = 0;
		sigsuspend (&suspend_signal_mask);
	} while (current->signal != restart_signal_num);

	/* Unblock the restart signal. */
	pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);

	if (current->async_target) {
#if MONO_ARCH_HAS_MONO_CONTEXT
		MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
		mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
		current->user_data = NULL;
		current->async_target = NULL;
		mono_monoctx_to_sigctx (&tmp, context);
#else
		g_error ("The new interruption machinery requires a working mono-context");
#endif
	}

	/* We're done resuming */
	mono_threads_notify_initiator_of_resume (current);

done:
	mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
	errno = old_errno;
}
示例#16
0
void
mono_thread_info_wait_for_resume (MonoThreadInfo* info)
{
	THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
	MONO_SEM_WAIT_UNITERRUPTIBLE (&info->resume_semaphore);
}
示例#17
0
static void
resume_blocking_suspended (MonoThreadInfo* info)
{
	THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
	mono_os_sem_post (&info->resume_semaphore);
}
示例#18
0
static void
resume_self_suspended (MonoThreadInfo* info)
{
	THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
	MONO_SEM_POST (&info->resume_semaphore);
}
static void
suspend_signal_handler (int _dummy, siginfo_t *info, void *context)
{
#if defined(__native_client__)
	g_assert_not_reached ();
#else
	int old_errno = errno;
	int hp_save_index = mono_hazard_pointer_save_for_signal_handler ();


	MonoThreadInfo *current = mono_thread_info_current ();
	gboolean ret;

	THREADS_SUSPEND_DEBUG ("SIGNAL HANDLER FOR %p [%p]\n", current, (void*)current->native_handle);
	if (current->syscall_break_signal) {
		current->syscall_break_signal = FALSE;
		THREADS_SUSPEND_DEBUG ("\tsyscall break for %p\n", current);
		mono_threads_notify_initiator_of_abort (current);
		goto done;
	}

	/* Have we raced with self suspend? */
	if (!mono_threads_transition_finish_async_suspend (current)) {
		current->suspend_can_continue = TRUE;
		THREADS_SUSPEND_DEBUG ("\tlost race with self suspend %p\n", current);
		goto done;
	}

	ret = mono_threads_get_runtime_callbacks ()->thread_state_init_from_sigctx (&current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX], context);

	/* thread_state_init_from_sigctx return FALSE if the current thread is detaching and suspend can't continue. */
	current->suspend_can_continue = ret;

	/*
	Block the restart signal.
	We need to block the restart signal while posting to the suspend_ack semaphore or we race to sigsuspend,
	which might miss the signal and get stuck.
	*/
	pthread_sigmask (SIG_BLOCK, &suspend_ack_signal_mask, NULL);

	/* This thread is doomed, all we can do is give up and let the suspender recover. */
	if (!ret) {
		THREADS_SUSPEND_DEBUG ("\tThread is dying, failed to capture state %p\n", current);
		mono_threads_transition_async_suspend_compensation (current);

		/* We're done suspending */
		mono_threads_notify_initiator_of_suspend (current);

		/* Unblock the restart signal. */
		pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);

		goto done;
	}

	/* We're done suspending */
	mono_threads_notify_initiator_of_suspend (current);

	do {
		current->signal = 0;
		sigsuspend (&suspend_signal_mask);
	} while (current->signal != restart_signal_num);

	/* Unblock the restart signal. */
	pthread_sigmask (SIG_UNBLOCK, &suspend_ack_signal_mask, NULL);

	if (current->async_target) {
#if MONO_ARCH_HAS_MONO_CONTEXT
		MonoContext tmp = current->thread_saved_state [ASYNC_SUSPEND_STATE_INDEX].ctx;
		mono_threads_get_runtime_callbacks ()->setup_async_callback (&tmp, current->async_target, current->user_data);
		current->async_target = current->user_data = NULL;
		mono_monoctx_to_sigctx (&tmp, context);
#else
		g_error ("The new interruption machinery requires a working mono-context");
#endif
	}

	/* We're done resuming */
	mono_threads_notify_initiator_of_resume (current);

done:
	mono_hazard_pointer_restore_for_signal_handler (hp_save_index);
	errno = old_errno;
#endif
}