Thread suspension, aborts, and state access

osfmk/kern/thread_act.c · 1570 lines · browse source

Per-thread user and internal suspend counts, waits for a remote thread to stop, abort and abort-safe handling, and the generic register-state access path. These operations are separate from replying to an outstanding Mach exception.

thread_hold source
Suspend execution of the specified thread. This is a recursive-style suspension of the thread, a count of suspends is maintained. Called with thread mutex held.
void
thread_hold(thread_t thread)
{
	if (thread->suspend_count++ == 0) {
		task_t task = get_threadtask(thread);
		thread_set_apc_ast(thread);
		assert(thread->suspend_parked == FALSE);

		KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_SUSPENSION, MACH_THREAD_SUSPEND) | DBG_FUNC_NONE,
		    thread->thread_id, thread->user_stop_count, task->pidsuspended);
	}
}
thread_release source
Decrement internal suspension count, setting thread runnable when count falls to zero. Because the wait is abortsafe, we can't be guaranteed that the thread is currently actually waiting even if suspend_parked is set. Called with thread mutex held.
void
thread_release(thread_t thread)
{
	assertf(thread->suspend_count > 0, "thread %p over-resumed", thread);

	/* fail-safe on non-assert builds */
	if (thread->suspend_count == 0) {
		return;
	}

	if (--thread->suspend_count == 0) {
		if (!thread->started) {
			thread_start(thread);
		} else if (thread->suspend_parked) {
			thread->suspend_parked = FALSE;
			thread_wakeup_thread(&thread->suspend_count, thread);
		}
		KDBG_RELEASE(MACHDBG_CODE(DBG_MACH_SUSPENSION, MACH_THREAD_RESUME) | DBG_FUNC_NONE, thread->thread_id);
	}
}
thread_suspend source · thread_suspend reference
kern_return_t
thread_suspend(thread_t thread)
{
	kern_return_t result = KERN_SUCCESS;

	if (thread == THREAD_NULL || get_threadtask(thread) == kernel_task) {
		return KERN_INVALID_ARGUMENT;
	}

	thread_mtx_lock(thread);

	if (thread->active) {
		if (thread->user_stop_count++ == 0) {
			thread_hold(thread);
		}
	} else {
		result = KERN_TERMINATED;
	}

	thread_mtx_unlock(thread);

	if (thread != current_thread() && result == KERN_SUCCESS) {
		thread_wait(thread, FALSE);
	}

	return result;
}
thread_resume source · thread_resume reference
kern_return_t
thread_resume(thread_t thread)
{
	kern_return_t result = KERN_SUCCESS;

	if (thread == THREAD_NULL || get_threadtask(thread) == kernel_task) {
		return KERN_INVALID_ARGUMENT;
	}

	thread_mtx_lock(thread);

	if (thread->active) {
		if (thread->user_stop_count > 0) {
			if (--thread->user_stop_count == 0) {
				thread_release(thread);
			}
		} else {
			result = KERN_FAILURE;
		}
	} else {
		result = KERN_TERMINATED;
	}

	thread_mtx_unlock(thread);

	return result;
}
thread_abort_safely source · thread_abort_safely reference
kern_return_t
thread_abort_safely(
	thread_t                thread)
{
	kern_return_t   result = KERN_SUCCESS;

	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}

	thread_mtx_lock(thread);

	if (thread->active) {
		spl_t           s = splsched();

		thread_lock(thread);
		if (!thread->at_safe_point ||
		    clear_wait_internal(thread, THREAD_INTERRUPTED) != KERN_SUCCESS) {
			if (!(thread->sched_flags & TH_SFLAG_ABORT)) {
				thread->sched_flags |= TH_SFLAG_ABORTED_MASK;
				thread_set_apc_ast_locked(thread);
				thread_depress_abort_locked(thread);
			}
		}
		thread_unlock(thread);
		splx(s);
	} else {
		result = KERN_TERMINATED;
	}
… more in source
thread_get_state_internal source
static inline kern_return_t
thread_get_state_internal(
	thread_t                thread,
	int                                             flavor,
	thread_state_t                  state,                  /* pointer to OUT array */
	mach_msg_type_number_t  *state_count,   /*IN/OUT*/
	thread_set_status_flags_t  flags)
{
	kern_return_t           result = KERN_SUCCESS;
	boolean_t               to_user = !!(flags & TSSF_TRANSLATE_TO_USER);

	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}

	thread_mtx_lock(thread);

	if (thread->active) {
		if (thread != current_thread()) {
			thread_hold(thread);

			thread_mtx_unlock(thread);

			if (thread_stop(thread, FALSE)) {
				thread_mtx_lock(thread);
				result = machine_thread_get_state(
					thread, flavor, state, state_count);
				thread_unstop(thread);
			} else {
				thread_mtx_lock(thread);
… more in source
thread_get_state source · thread_get_state reference
kern_return_t
thread_get_state(
	thread_t                thread,
	int                                             flavor,
	thread_state_t                  state,                  /* pointer to OUT array */
	mach_msg_type_number_t  *state_count)   /*IN/OUT*/
{
	return thread_get_state_internal(thread, flavor, state, state_count, TSSF_FLAGS_NONE);
}
thread_get_state_to_user source
kern_return_t
thread_get_state_to_user(
	thread_t                thread,
	int                                             flavor,
	thread_state_t                  state,                  /* pointer to OUT array */
	mach_msg_type_number_t  *state_count)   /*IN/OUT*/
{
	return thread_get_state_internal(thread, flavor, state, state_count, TSSF_TRANSLATE_TO_USER);
}
thread_set_state_internal source
Change thread's machine-dependent state. Called with nothing locked. Returns same way.
static inline kern_return_t
thread_set_state_internal(
	thread_t                        thread,
	int                             flavor,
	thread_state_t                  state,
	mach_msg_type_number_t          state_count,
	thread_state_t                  old_state,
	mach_msg_type_number_t          old_state_count,
	thread_set_status_flags_t       flags)
{
	kern_return_t           result = KERN_SUCCESS;
	boolean_t               from_user = !!(flags & TSSF_TRANSLATE_TO_USER);

	if (thread == THREAD_NULL) {
		return KERN_INVALID_ARGUMENT;
	}

	/*
	 * process will be crashed with kGUARD_EXC_THREAD_SET_STATE
	 * if thread_set_state_allowed() return false.
	 */
	if (!thread_set_state_allowed(thread, flavor, flags)) {
		return KERN_NO_ACCESS;
	}

	thread_mtx_lock(thread);

	if (thread->active) {
		if (from_user) {
			result = machine_thread_state_convert_from_user(thread, flavor,
… more in source
thread_set_state source · thread_set_state reference
kern_return_t
thread_set_state(
	thread_t                thread,
	int                                             flavor,
	thread_state_t                  state,
	mach_msg_type_number_t  state_count)
{
	return thread_set_state_internal(thread, flavor, state, state_count, NULL, 0, TSSF_FLAGS_NONE);
}
thread_set_state_from_user source
kern_return_t
thread_set_state_from_user(
	thread_t                thread,
	int                                             flavor,
	thread_state_t                  state,
	mach_msg_type_number_t  state_count)
{
	return thread_set_state_internal(thread, flavor, state, state_count, NULL,
	           0, TSSF_TRANSLATE_TO_USER | TSSF_CHECK_ENTITLEMENT);
}