#include <mach/mach_types.h>
#include <mach/kern_return.h>
#include <mach/alert.h>
#include <mach/rpc.h>
#include <mach/thread_act_server.h>
#include <kern/kern_types.h>
#include <kern/ast.h>
#include <kern/mach_param.h>
#include <kern/zalloc.h>
#include <kern/extmod_statistics.h>
#include <kern/thread.h>
#include <kern/task.h>
#include <kern/sched_prim.h>
#include <kern/misc_protos.h>
#include <kern/assert.h>
#include <kern/exception.h>
#include <kern/ipc_mig.h>
#include <kern/ipc_tt.h>
#include <kern/machine.h>
#include <kern/spl.h>
#include <kern/syscall_subr.h>
#include <kern/sync_lock.h>
#include <kern/processor.h>
#include <kern/timer.h>
#include <kern/affinity.h>
#include <mach/rpc.h>
void act_abort(thread_t);
void install_special_handler_locked(thread_t);
void special_handler_continue(void);
void
thread_start_internal(
thread_t thread)
{
clear_wait(thread, THREAD_AWAKENED);
thread->started = TRUE;
}
kern_return_t
thread_terminate_internal(
thread_t thread)
{
kern_return_t result = KERN_SUCCESS;
thread_mtx_lock(thread);
if (thread->active) {
thread->active = FALSE;
act_abort(thread);
if (thread->started)
clear_wait(thread, THREAD_INTERRUPTED);
else {
thread_start_internal(thread);
}
}
else
result = KERN_TERMINATED;
if (thread->affinity_set != NULL)
thread_affinity_terminate(thread);
thread_mtx_unlock(thread);
if (thread != current_thread() && result == KERN_SUCCESS)
thread_wait(thread);
return (result);
}
kern_return_t
thread_terminate(
thread_t thread)
{
kern_return_t result;
if (thread == THREAD_NULL)
return (KERN_INVALID_ARGUMENT);
if ( thread->task == kernel_task &&
thread != current_thread() )
return (KERN_FAILURE);
result = thread_terminate_internal(thread);
if (thread->task == kernel_task) {
ml_set_interrupts_enabled(FALSE);
ast_taken(AST_APC, TRUE);
panic("thread_terminate");
}
return (result);
}
void
thread_hold(
register thread_t thread)
{
if (thread->suspend_count++ == 0) {
install_special_handler(thread);
if (thread->started)
thread_wakeup_one(&thread->suspend_count);
}
}
void
thread_release(
register thread_t thread)
{
if ( thread->suspend_count > 0 &&
--thread->suspend_count == 0 ) {
if (thread->started)
thread_wakeup_one(&thread->suspend_count);
else {
thread_start_internal(thread);
}
}
}
kern_return_t
thread_suspend(
register thread_t thread)
{
thread_t self = current_thread();
kern_return_t result = KERN_SUCCESS;
if (thread == THREAD_NULL || thread->task == kernel_task)
return (KERN_INVALID_ARGUMENT);
thread_mtx_lock(thread);
if (thread->active) {
if ( thread->user_stop_count++ == 0 &&
thread->suspend_count++ == 0 ) {
install_special_handler(thread);
if (thread != self)
thread_wakeup_one(&thread->suspend_count);
}
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
if (thread != self && result == KERN_SUCCESS)
thread_wait(thread);
return (result);
}
kern_return_t
thread_resume(
register thread_t thread)
{
kern_return_t result = KERN_SUCCESS;
if (thread == THREAD_NULL || thread->task == 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->suspend_count == 0 ) {
if (thread->started)
thread_wakeup_one(&thread->suspend_count);
else {
thread_start_internal(thread);
}
}
}
else
result = KERN_FAILURE;
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
kern_return_t
thread_depress_abort(
register thread_t thread)
{
kern_return_t result;
if (thread == THREAD_NULL)
return (KERN_INVALID_ARGUMENT);
thread_mtx_lock(thread);
if (thread->active)
result = thread_depress_abort_internal(thread);
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
void
act_abort(
thread_t thread)
{
spl_t s = splsched();
thread_lock(thread);
if (!(thread->sched_flags & TH_SFLAG_ABORT)) {
thread->sched_flags |= TH_SFLAG_ABORT;
install_special_handler_locked(thread);
}
else
thread->sched_flags &= ~TH_SFLAG_ABORTSAFELY;
thread_unlock(thread);
splx(s);
}
kern_return_t
thread_abort(
register thread_t thread)
{
kern_return_t result = KERN_SUCCESS;
if (thread == THREAD_NULL)
return (KERN_INVALID_ARGUMENT);
thread_mtx_lock(thread);
if (thread->active) {
act_abort(thread);
clear_wait(thread, THREAD_INTERRUPTED);
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
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;
install_special_handler_locked(thread);
}
}
thread_unlock(thread);
splx(s);
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
#include <mach/thread_info.h>
#include <mach/thread_special_ports.h>
#include <ipc/ipc_port.h>
kern_return_t
thread_info(
thread_t thread,
thread_flavor_t flavor,
thread_info_t thread_info_out,
mach_msg_type_number_t *thread_info_count)
{
kern_return_t result;
if (thread == THREAD_NULL)
return (KERN_INVALID_ARGUMENT);
thread_mtx_lock(thread);
if (thread->active)
result = thread_info_internal(
thread, flavor, thread_info_out, thread_info_count);
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
kern_return_t
thread_get_state(
register thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t *state_count)
{
kern_return_t result = KERN_SUCCESS;
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)) {
thread_mtx_lock(thread);
result = machine_thread_get_state(
thread, flavor, state, state_count);
thread_unstop(thread);
}
else {
thread_mtx_lock(thread);
result = KERN_ABORTED;
}
thread_release(thread);
}
else
result = machine_thread_get_state(
thread, flavor, state, state_count);
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
static kern_return_t
thread_set_state_internal(
register thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t state_count,
boolean_t from_user)
{
kern_return_t result = KERN_SUCCESS;
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)) {
thread_mtx_lock(thread);
result = machine_thread_set_state(
thread, flavor, state, state_count);
thread_unstop(thread);
}
else {
thread_mtx_lock(thread);
result = KERN_ABORTED;
}
thread_release(thread);
}
else
result = machine_thread_set_state(
thread, flavor, state, state_count);
}
else
result = KERN_TERMINATED;
if ((result == KERN_SUCCESS) && from_user)
extmod_statistics_incr_thread_set_state(thread);
thread_mtx_unlock(thread);
return (result);
}
kern_return_t
thread_set_state(
register thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t state_count);
kern_return_t
thread_set_state(
register 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, FALSE);
}
kern_return_t
thread_set_state_from_user(
register 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, TRUE);
}
kern_return_t
thread_state_initialize(
register thread_t thread)
{
kern_return_t result = KERN_SUCCESS;
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)) {
thread_mtx_lock(thread);
result = machine_thread_state_initialize( thread );
thread_unstop(thread);
}
else {
thread_mtx_lock(thread);
result = KERN_ABORTED;
}
thread_release(thread);
}
else
result = machine_thread_state_initialize( thread );
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(thread);
return (result);
}
kern_return_t
thread_dup(
register thread_t target)
{
thread_t self = current_thread();
kern_return_t result = KERN_SUCCESS;
if (target == THREAD_NULL || target == self)
return (KERN_INVALID_ARGUMENT);
thread_mtx_lock(target);
if (target->active) {
thread_hold(target);
thread_mtx_unlock(target);
if (thread_stop(target)) {
thread_mtx_lock(target);
result = machine_thread_dup(self, target);
if (self->affinity_set != AFFINITY_SET_NULL)
thread_affinity_dup(self, target);
thread_unstop(target);
}
else {
thread_mtx_lock(target);
result = KERN_ABORTED;
}
thread_release(target);
}
else
result = KERN_TERMINATED;
thread_mtx_unlock(target);
return (result);
}
kern_return_t
thread_setstatus(
register thread_t thread,
int flavor,
thread_state_t tstate,
mach_msg_type_number_t count)
{
return (thread_set_state(thread, flavor, tstate, count));
}
kern_return_t
thread_getstatus(
register thread_t thread,
int flavor,
thread_state_t tstate,
mach_msg_type_number_t *count)
{
return (thread_get_state(thread, flavor, tstate, count));
}
void
install_special_handler(
thread_t thread)
{
spl_t s = splsched();
thread_lock(thread);
install_special_handler_locked(thread);
thread_unlock(thread);
splx(s);
}
void
install_special_handler_locked(
thread_t thread)
{
ReturnHandler **rh;
for (rh = &thread->handlers; *rh; rh = &(*rh)->next)
continue;
if (rh != &thread->special_handler.next)
*rh = &thread->special_handler;
if (thread->sched_flags & TH_SFLAG_DEPRESSED_MASK)
SCHED(compute_priority)(thread, TRUE);
thread_ast_set(thread, AST_APC);
if (thread == current_thread())
ast_propagate(thread->ast);
else {
processor_t processor = thread->last_processor;
if ( processor != PROCESSOR_NULL &&
processor->state == PROCESSOR_RUNNING &&
processor->active_thread == thread )
cause_ast_check(processor);
}
}
void
act_execute_returnhandlers(void)
{
thread_t thread = current_thread();
thread_ast_clear(thread, AST_APC);
spllo();
for (;;) {
ReturnHandler *rh;
thread_mtx_lock(thread);
(void)splsched();
thread_lock(thread);
rh = thread->handlers;
if (rh != NULL) {
thread->handlers = rh->next;
thread_unlock(thread);
spllo();
thread_mtx_unlock(thread);
(*rh->handler)(rh, thread);
}
else
break;
}
thread_unlock(thread);
spllo();
thread_mtx_unlock(thread);
}
void
special_handler_continue(void)
{
thread_t thread = current_thread();
thread_mtx_lock(thread);
if (thread->suspend_count > 0)
install_special_handler(thread);
else {
spl_t s = splsched();
thread_lock(thread);
if (thread->sched_flags & TH_SFLAG_DEPRESSED_MASK) {
processor_t myprocessor = thread->last_processor;
thread->sched_pri = DEPRESSPRI;
myprocessor->current_pri = thread->sched_pri;
}
thread_unlock(thread);
splx(s);
}
thread_mtx_unlock(thread);
thread_exception_return();
}
void
special_handler(
__unused ReturnHandler *rh,
thread_t thread)
{
spl_t s;
thread_mtx_lock(thread);
s = splsched();
thread_lock(thread);
thread->sched_flags &= ~TH_SFLAG_ABORTED_MASK;
thread_unlock(thread);
splx(s);
if (thread->active) {
if (thread->suspend_count > 0) {
if (thread->handlers == NULL) {
assert_wait(&thread->suspend_count, THREAD_ABORTSAFE);
thread_mtx_unlock(thread);
thread_block((thread_continue_t)special_handler_continue);
}
thread_mtx_unlock(thread);
special_handler_continue();
}
}
else {
thread_mtx_unlock(thread);
thread_terminate_self();
}
thread_mtx_unlock(thread);
}
kern_return_t
act_set_state(
thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t count);
kern_return_t
act_set_state(
thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t count)
{
if (thread == current_thread())
return (KERN_INVALID_ARGUMENT);
return (thread_set_state(thread, flavor, state, count));
}
kern_return_t
act_set_state_from_user(
thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t count)
{
if (thread == current_thread())
return (KERN_INVALID_ARGUMENT);
return (thread_set_state_from_user(thread, flavor, state, count));
}
kern_return_t
act_get_state(
thread_t thread,
int flavor,
thread_state_t state,
mach_msg_type_number_t *count)
{
if (thread == current_thread())
return (KERN_INVALID_ARGUMENT);
return (thread_get_state(thread, flavor, state, count));
}
void
act_set_astbsd(
thread_t thread)
{
spl_t s = splsched();
if (thread == current_thread()) {
thread_ast_set(thread, AST_BSD);
ast_propagate(thread->ast);
}
else {
processor_t processor;
thread_lock(thread);
thread_ast_set(thread, AST_BSD);
processor = thread->last_processor;
if ( processor != PROCESSOR_NULL &&
processor->state == PROCESSOR_RUNNING &&
processor->active_thread == thread )
cause_ast_check(processor);
thread_unlock(thread);
}
splx(s);
}
void
act_set_apc(
thread_t thread)
{
spl_t s = splsched();
if (thread == current_thread()) {
thread_ast_set(thread, AST_APC);
ast_propagate(thread->ast);
}
else {
processor_t processor;
thread_lock(thread);
thread_ast_set(thread, AST_APC);
processor = thread->last_processor;
if ( processor != PROCESSOR_NULL &&
processor->state == PROCESSOR_RUNNING &&
processor->active_thread == thread )
cause_ast_check(processor);
thread_unlock(thread);
}
splx(s);
}