X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/1c79356b52d46aa6b508fb032f5ae709b1f2897b..d7e50217d7adf6e52786a38bcaa4cd698cb9a79e:/osfmk/kern/thread_act.c diff --git a/osfmk/kern/thread_act.c b/osfmk/kern/thread_act.c index 459cf608a..129f39d9a 100644 --- a/osfmk/kern/thread_act.c +++ b/osfmk/kern/thread_act.c @@ -3,19 +3,22 @@ * * @APPLE_LICENSE_HEADER_START@ * - * The contents of this file constitute Original Code as defined in and - * are subject to the Apple Public Source License Version 1.1 (the - * "License"). You may not use this file except in compliance with the - * License. Please obtain a copy of the License at - * http://www.apple.com/publicsource and read it before using this file. + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * - * This Original Code and all software distributed under the License are - * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the - * License for the specific language governing rights and limitations - * under the License. + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ @@ -56,7 +59,6 @@ #include #include #include -#include #include #include #include @@ -68,8 +70,8 @@ #include #include #include -#include #include /*** ??? fix so this can be removed ***/ +#include #include #include @@ -85,26 +87,30 @@ unsigned int watchacts = 0 /* WA_ALL */ * Track the number of times we need to swapin a thread to deallocate it. */ int act_free_swapin = 0; +boolean_t first_act; /* * Forward declarations for functions local to this file. */ -kern_return_t act_abort( thread_act_t, int); +kern_return_t act_abort( thread_act_t, boolean_t); void special_handler(ReturnHandler *, thread_act_t); -void nudge(thread_act_t); kern_return_t act_set_state_locked(thread_act_t, int, thread_state_t, mach_msg_type_number_t); kern_return_t act_get_state_locked(thread_act_t, int, thread_state_t, mach_msg_type_number_t *); +void act_set_astbsd(thread_act_t); void act_set_apc(thread_act_t); -void act_clr_apc(thread_act_t); void act_user_to_kernel(thread_act_t); void act_ulock_release_all(thread_act_t thr_act); void install_special_handler_locked(thread_act_t); +static void act_disable(thread_act_t); + +struct thread_activation pageout_act; + static zone_t thr_act_zone; /* @@ -114,96 +120,65 @@ static zone_t thr_act_zone; /* * Internal routine to terminate a thread. - * Called with task locked. + * Sometimes called with task already locked. */ kern_return_t thread_terminate_internal( - register thread_act_t thr_act) + register thread_act_t act) { - thread_t thread; - task_t task; - struct ipc_port *iplock; - kern_return_t ret; -#if NCPUS > 1 - boolean_t held; -#endif /* NCPUS > 1 */ + kern_return_t result; + thread_t thread; -#if THREAD_SWAPPER - thread_swap_disable(thr_act); -#endif /* THREAD_SWAPPER */ + thread = act_lock_thread(act); - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } -#if NCPUS > 1 + act_disable(act); + result = act_abort(act, FALSE); + /* * Make sure this thread enters the kernel + * Must unlock the act, but leave the shuttle + * captured in this act. */ if (thread != current_thread()) { - thread_hold(thr_act); - act_unlock_thread(thr_act); + act_unlock(act); - if (!thread_stop_wait(thread)) { - ret = KERN_ABORTED; - (void)act_lock_thread(thr_act); - thread_release(thr_act); - act_unlock_thread(thr_act); - return (ret); - } + if (thread_stop(thread)) + thread_unstop(thread); + else + result = KERN_ABORTED; - held = TRUE; - (void)act_lock_thread(thr_act); - } else { - held = FALSE; + act_lock(act); } -#endif /* NCPUS > 1 */ - assert(thr_act->active); - act_disable_task_locked(thr_act); - ret = act_abort(thr_act,FALSE); - act_unlock_thread(thr_act); + clear_wait(thread, act->inited? THREAD_INTERRUPTED: THREAD_AWAKENED); + act_unlock_thread(act); -#if NCPUS > 1 - if (held) { - thread_unstop(thread); - (void)act_lock_thread(thr_act); - thread_release(thr_act); - act_unlock_thread(thr_act); - } -#endif /* NCPUS > 1 */ - return(ret); + return (result); } /* - * Terminate a thread. Called with nothing locked. - * Returns same way. + * Terminate a thread. */ kern_return_t thread_terminate( - register thread_act_t thr_act) + register thread_act_t act) { - task_t task; - kern_return_t ret; + kern_return_t result; - if (thr_act == THR_ACT_NULL) - return KERN_INVALID_ARGUMENT; + if (act == THR_ACT_NULL) + return (KERN_INVALID_ARGUMENT); - task = thr_act->task; - if (((task == kernel_task) || (thr_act->kernel_loaded == TRUE)) - && (current_act() != thr_act)) { - return(KERN_FAILURE); - } + if ( (act->task == kernel_task || + act->kernel_loaded ) && + act != current_act() ) + return (KERN_FAILURE); - /* - * Take the task lock and then call the internal routine - * that terminates a thread (it needs the task locked). - */ - task_lock(task); - ret = thread_terminate_internal(thr_act); - task_unlock(task); + result = thread_terminate_internal(act); /* * If a kernel thread is terminating itself, force an AST here. @@ -211,32 +186,35 @@ thread_terminate( * code - and all threads finish their own termination in the * special handler APC. */ - if (((thr_act->task == kernel_task) || (thr_act->kernel_loaded == TRUE)) - && (current_act() == thr_act)) { - ast_taken(FALSE, AST_APC, 0); - panic("thread_terminate(): returning from ast_taken() for %x kernel activation\n", thr_act); - } + if ( act->task == kernel_task || + act->kernel_loaded ) { + assert(act == current_act()); + ast_taken(AST_APC, FALSE); + panic("thread_terminate"); + } - return ret; + return (result); } /* - * thread_hold: - * - * Suspend execution of the specified thread. - * This is a recursive-style suspension of the thread, a count of - * suspends is maintained. + * Suspend execution of the specified thread. + * This is a recursive-style suspension of the thread, a count of + * suspends is maintained. * - * Called with thr_act locked "appropriately" for synchrony with - * RPC (see act_lock_thread()). Returns same way. + * Called with act_lock held. */ void thread_hold( - register thread_act_t thr_act) + register thread_act_t act) { - if (thr_act->suspend_count++ == 0) { - install_special_handler(thr_act); - nudge(thr_act); + thread_t thread = act->thread; + + if (act->suspend_count++ == 0) { + install_special_handler(act); + if ( act->inited && + thread != THREAD_NULL && + thread->top_act == act ) + thread_wakeup_one(&act->suspend_count); } } @@ -244,84 +222,99 @@ thread_hold( * Decrement internal suspension count for thr_act, setting thread * runnable when count falls to zero. * - * Called with thr_act locked "appropriately" for synchrony - * with RPC (see act_lock_thread()). + * Called with act_lock held. */ void thread_release( - register thread_act_t thr_act) + register thread_act_t act) { - if( thr_act->suspend_count && - (--thr_act->suspend_count == 0) ) - nudge( thr_act ); + thread_t thread = act->thread; + + if ( act->suspend_count > 0 && + --act->suspend_count == 0 && + thread != THREAD_NULL && + thread->top_act == act ) { + if (!act->inited) { + clear_wait(thread, THREAD_AWAKENED); + act->inited = TRUE; + } + else + thread_wakeup_one(&act->suspend_count); + } } kern_return_t thread_suspend( - register thread_act_t thr_act) + register thread_act_t act) { - thread_t thread; + thread_t thread; - if (thr_act == THR_ACT_NULL) { - return(KERN_INVALID_ARGUMENT); - } - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); + if (act == THR_ACT_NULL) + return (KERN_INVALID_ARGUMENT); + + thread = act_lock_thread(act); + + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } - if (thr_act->user_stop_count++ == 0 && - thr_act->suspend_count++ == 0 ) { - install_special_handler(thr_act); - if (thread && - thr_act == thread->top_act && thread != current_thread()) { - nudge(thr_act); - act_unlock_thread(thr_act); - (void)thread_wait(thread); - } - else { - /* - * No need to wait for target thread - */ - act_unlock_thread(thr_act); + + if ( act->user_stop_count++ == 0 && + act->suspend_count++ == 0 ) { + install_special_handler(act); + if ( thread != current_thread() && + thread != THREAD_NULL && + thread->top_act == act ) { + assert(act->inited); + thread_wakeup_one(&act->suspend_count); + act_unlock_thread(act); + + thread_wait(thread); } + else + act_unlock_thread(act); } - else { - /* - * Thread is already suspended - */ - act_unlock_thread(thr_act); - } - return(KERN_SUCCESS); + else + act_unlock_thread(act); + + return (KERN_SUCCESS); } kern_return_t thread_resume( - register thread_act_t thr_act) + register thread_act_t act) { - register kern_return_t ret; - spl_t s; + kern_return_t result = KERN_SUCCESS; thread_t thread; - if (thr_act == THR_ACT_NULL) - return(KERN_INVALID_ARGUMENT); - thread = act_lock_thread(thr_act); - ret = KERN_SUCCESS; + if (act == THR_ACT_NULL) + return (KERN_INVALID_ARGUMENT); - if (thr_act->active) { - if (thr_act->user_stop_count > 0) { - if( --thr_act->user_stop_count == 0 ) { - --thr_act->suspend_count; - nudge( thr_act ); + thread = act_lock_thread(act); + + if (act->active) { + if (act->user_stop_count > 0) { + if ( --act->user_stop_count == 0 && + --act->suspend_count == 0 && + thread != THREAD_NULL && + thread->top_act == act ) { + if (!act->inited) { + clear_wait(thread, THREAD_AWAKENED); + act->inited = TRUE; + } + else + thread_wakeup_one(&act->suspend_count); } } else - ret = KERN_FAILURE; + result = KERN_FAILURE; } else - ret = KERN_TERMINATED; - act_unlock_thread( thr_act ); - return ret; + result = KERN_TERMINATED; + + act_unlock_thread(act); + + return (result); } /* @@ -333,24 +326,10 @@ thread_resume( */ kern_return_t post_alert( - register thread_act_t thr_act, - unsigned alert_bits ) + register thread_act_t act, + unsigned alert_bits) { - thread_act_t next; - thread_t thread; - - /* - * Chase the chain, setting alert bits and installing - * special handlers for each thread act. - */ - /*** Not yet SMP safe ***/ - /*** Worse, where's the activation locking as the chain is walked? ***/ - for (next = thr_act; next != THR_ACT_NULL; next = next->higher) { - next->alerts |= alert_bits; - install_special_handler_locked(next); - } - - return(KERN_SUCCESS); + panic("post_alert"); } /* @@ -364,8 +343,6 @@ thread_depress_abort( { register thread_t thread; kern_return_t result; - sched_policy_t *policy; - spl_t s; if (thr_act == THR_ACT_NULL) return (KERN_INVALID_ARGUMENT); @@ -378,13 +355,7 @@ thread_depress_abort( return (KERN_TERMINATED); } - s = splsched(); - thread_lock(thread); - policy = &sched_policy[thread->policy]; - thread_unlock(thread); - splx(s); - - result = policy->sp_ops.sp_thread_depress_abort(policy, thread); + result = _mk_sp_thread_depress_abort(thread, FALSE); act_unlock_thread(thr_act); @@ -393,197 +364,91 @@ thread_depress_abort( /* - * Already locked: all RPC-related locks for thr_act (see - * act_lock_thread()). + * Indicate that the activation should run its + * special handler to detect the condition. + * + * Called with act_lock held. */ kern_return_t -act_abort( thread_act_t thr_act, int chain_break ) +act_abort( + thread_act_t act, + boolean_t chain_break ) { - spl_t spl; - thread_t thread; - struct ipc_port *iplock = thr_act->pool_port; - thread_act_t orphan; - kern_return_t kr; - etap_data_t probe_data; - - ETAP_DATA_LOAD(probe_data[0], thr_act); - ETAP_DATA_LOAD(probe_data[1], thr_act->thread); - ETAP_PROBE_DATA(ETAP_P_ACT_ABORT, - 0, - current_thread(), - &probe_data, - ETAP_DATA_ENTRY*2); - - /* - * If the target thread activation is not the head... - */ - if ( thr_act->thread->top_act != thr_act ) { - /* - * mark the activation for abort, - * update the suspend count, - * always install the special handler - */ - install_special_handler(thr_act); - -#ifdef AGRESSIVE_ABORT - /* release state buffer for target's outstanding invocation */ - if (unwind_invoke_state(thr_act) != KERN_SUCCESS) { - panic("unwind_invoke_state failure"); - } - - /* release state buffer for target's incoming invocation */ - if (thr_act->lower != THR_ACT_NULL) { - if (unwind_invoke_state(thr_act->lower) - != KERN_SUCCESS) { - panic("unwind_invoke_state failure"); - } - } - - /* unlink target thread activation from shuttle chain */ - if ( thr_act->lower == THR_ACT_NULL ) { - /* - * This is the root thread activation of the chain. - * Unlink the root thread act from the bottom of - * the chain. - */ - thr_act->higher->lower = THR_ACT_NULL; - } else { - /* - * This thread act is in the middle of the chain. - * Unlink the thread act from the middle of the chain. - */ - thr_act->higher->lower = thr_act->lower; - thr_act->lower->higher = thr_act->higher; - - /* set the terminated bit for RPC return processing */ - thr_act->lower->alerts |= SERVER_TERMINATED; - } - - orphan = thr_act->higher; - - /* remove the activation from its thread pool */ - /* (note: this is okay for "rooted threads," too) */ - act_locked_act_set_thread_pool(thr_act, IP_NULL); - - /* (just to be thorough) release the IP lock */ - if (iplock != IP_NULL) ip_unlock(iplock); - - /* release one more reference for a rooted thread */ - if (iplock == IP_NULL) act_locked_act_deallocate(thr_act); - - /* Presumably, the only reference to this activation is - * now held by the caller of this routine. */ - assert(thr_act->ref_count == 1); -#else /*AGRESSIVE_ABORT*/ - /* If there is a lower activation in the RPC chain... */ - if (thr_act->lower != THR_ACT_NULL) { - /* ...indicate the server activation was terminated */ - thr_act->lower->alerts |= SERVER_TERMINATED; - } - /* Mark (and process) any orphaned activations */ - orphan = thr_act->higher; -#endif /*AGRESSIVE_ABORT*/ + thread_t thread = act->thread; + spl_t s = splsched(); - /* indicate client of orphaned chain has been terminated */ - orphan->alerts |= CLIENT_TERMINATED; + assert(thread->top_act == act); - /* - * Set up posting of alert to headward portion of - * the RPC chain. - */ - /*** fix me -- orphan act is not locked ***/ - post_alert(orphan, ORPHANED); - - /* - * Get attention of head of RPC chain. - */ - nudge(thr_act->thread->top_act); - return (KERN_SUCCESS); + thread_lock(thread); + if (!(thread->state & TH_ABORT)) { + thread->state |= TH_ABORT; + install_special_handler_locked(act); + } else { + thread->state &= ~TH_ABORT_SAFELY; } + thread_unlock(thread); + splx(s); - /* - * If the target thread is the end of the chain, the thread - * has to be marked for abort and rip it out of any wait. - */ - spl = splsched(); - thread_lock(thr_act->thread); - if (thr_act->thread->top_act == thr_act) { - thr_act->thread->state |= TH_ABORT; - if (thr_act->thread->state & TH_ABORT) - clear_wait_internal(thr_act->thread, THREAD_INTERRUPTED); - thread_unlock(thr_act->thread); - splx(spl); - install_special_handler(thr_act); - nudge( thr_act ); - } - return KERN_SUCCESS; + return (KERN_SUCCESS); } kern_return_t thread_abort( - register thread_act_t thr_act) + register thread_act_t act) { - int ret; + kern_return_t result; thread_t thread; - if (thr_act == THR_ACT_NULL || thr_act == current_act()) + if (act == THR_ACT_NULL) return (KERN_INVALID_ARGUMENT); - /* - * Lock the target thread and the current thread now, - * in case thread_halt() ends up being called below. - */ - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); + + thread = act_lock_thread(act); + + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } - ret = act_abort( thr_act, FALSE ); - act_unlock_thread( thr_act ); - return ret; + result = act_abort(act, FALSE); + clear_wait(thread, THREAD_INTERRUPTED); + act_unlock_thread(act); + + return (result); } kern_return_t thread_abort_safely( - register thread_act_t thr_act) + thread_act_t act) { thread_t thread; + kern_return_t ret; spl_t s; - if (thr_act == THR_ACT_NULL || thr_act == current_act()) - return(KERN_INVALID_ARGUMENT); + if ( act == THR_ACT_NULL ) + return (KERN_INVALID_ARGUMENT); - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); - } - if (thread->top_act != thr_act) { - act_unlock_thread(thr_act); - return(KERN_FAILURE); + thread = act_lock_thread(act); + + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } + s = splsched(); thread_lock(thread); - - if ( thread->at_safe_point ) { - /* - * It's an abortable wait, clear it, then - * let the thread go and return successfully. - */ - clear_wait_internal(thread, THREAD_INTERRUPTED); - thread_unlock(thread); - act_unlock_thread(thr_act); - splx(s); - return KERN_SUCCESS; + if (!thread->at_safe_point || + clear_wait_internal(thread, THREAD_INTERRUPTED) != KERN_SUCCESS) { + if (!(thread->state & TH_ABORT)) { + thread->state |= (TH_ABORT|TH_ABORT_SAFELY); + install_special_handler_locked(act); + } } - - /* - * if not stopped at a safepoint, just let it go and return failure. - */ thread_unlock(thread); - act_unlock_thread(thr_act); splx(s); - return KERN_FAILURE; + + act_unlock_thread(act); + + return (KERN_SUCCESS); } /*** backward compatibility hacks ***/ @@ -742,47 +607,60 @@ thread_set_special_port( */ kern_return_t thread_get_state( - register thread_act_t thr_act, - int flavor, - thread_state_t state, /* pointer to OUT array */ + register thread_act_t act, + int flavor, + thread_state_t state, /* pointer to OUT array */ mach_msg_type_number_t *state_count) /*IN/OUT*/ { - kern_return_t ret; - thread_t thread, nthread; + kern_return_t result = KERN_SUCCESS; + thread_t thread; -#if 0 /* Grenoble - why?? */ - if (thr_act == THR_ACT_NULL || thr_act == current_act()) -#else - if (thr_act == THR_ACT_NULL) -#endif + if (act == THR_ACT_NULL || act == current_act()) return (KERN_INVALID_ARGUMENT); - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); + thread = act_lock_thread(act); + + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } - thread_hold(thr_act); - while (1) { - if (!thread || thr_act != thread->top_act) + thread_hold(act); + + for (;;) { + thread_t thread1; + + if ( thread == THREAD_NULL || + thread->top_act != act ) break; - act_unlock_thread(thr_act); - (void)thread_stop_wait(thread); - nthread = act_lock_thread(thr_act); - if (nthread == thread) + act_unlock_thread(act); + + if (!thread_stop(thread)) { + result = KERN_ABORTED; + (void)act_lock_thread(act); + thread = THREAD_NULL; break; + } + + thread1 = act_lock_thread(act); + if (thread1 == thread) + break; + thread_unstop(thread); - thread = nthread; + thread = thread1; } - ret = act_machine_get_state(thr_act, flavor, - state, state_count); - if (thread && thr_act == thread->top_act) - thread_unstop(thread); - thread_release(thr_act); - act_unlock_thread(thr_act); - return(ret); + if (result == KERN_SUCCESS) + result = act_machine_get_state(act, flavor, state, state_count); + + if ( thread != THREAD_NULL && + thread->top_act == act ) + thread_unstop(thread); + + thread_release(act); + act_unlock_thread(act); + + return (result); } /* @@ -791,53 +669,60 @@ thread_get_state( */ kern_return_t thread_set_state( - register thread_act_t thr_act, - int flavor, - thread_state_t state, + register thread_act_t act, + int flavor, + thread_state_t state, mach_msg_type_number_t state_count) { - kern_return_t ret; - thread_t thread, nthread; + kern_return_t result = KERN_SUCCESS; + thread_t thread; -#if 0 /* Grenoble - why?? */ - if (thr_act == THR_ACT_NULL || thr_act == current_act()) -#else - if (thr_act == THR_ACT_NULL) -#endif + if (act == THR_ACT_NULL || act == current_act()) return (KERN_INVALID_ARGUMENT); - /* - * We have no kernel activations, so Utah's MO fails for signals etc. - * - * If we're blocked in the kernel, use non-blocking method, else - * pass locked thr_act+thread in to "normal" act_[gs]et_state(). - */ - thread = act_lock_thread(thr_act); - if (!thr_act->active) { - act_unlock_thread(thr_act); - return(KERN_TERMINATED); + thread = act_lock_thread(act); + + if (!act->active) { + act_unlock_thread(act); + return (KERN_TERMINATED); } - thread_hold(thr_act); - while (1) { - if (!thread || thr_act != thread->top_act) + thread_hold(act); + + for (;;) { + thread_t thread1; + + if ( thread == THREAD_NULL || + thread->top_act != act ) break; - act_unlock_thread(thr_act); - (void)thread_stop_wait(thread); - nthread = act_lock_thread(thr_act); - if (nthread == thread) + act_unlock_thread(act); + + if (!thread_stop(thread)) { + result = KERN_ABORTED; + (void)act_lock_thread(act); + thread = THREAD_NULL; break; + } + + thread1 = act_lock_thread(act); + if (thread1 == thread) + break; + thread_unstop(thread); - thread = nthread; + thread = thread1; } - ret = act_machine_set_state(thr_act, flavor, - state, state_count); - if (thread && thr_act == thread->top_act) + + if (result == KERN_SUCCESS) + result = act_machine_set_state(act, flavor, state, state_count); + + if ( thread != THREAD_NULL && + thread->top_act == act ) thread_unstop(thread); - thread_release(thr_act); - act_unlock_thread(thr_act); - return(ret); + thread_release(act); + act_unlock_thread(act); + + return (result); } /* @@ -846,40 +731,58 @@ thread_set_state( kern_return_t thread_dup( - thread_act_t source_thr_act, - thread_act_t target_thr_act) + register thread_act_t target) { - kern_return_t ret; - thread_t thread, nthread; + kern_return_t result = KERN_SUCCESS; + thread_act_t self = current_act(); + thread_t thread; - if (target_thr_act == THR_ACT_NULL || target_thr_act == current_act()) + if (target == THR_ACT_NULL || target == self) return (KERN_INVALID_ARGUMENT); - thread = act_lock_thread(target_thr_act); - if (!target_thr_act->active) { - act_unlock_thread(target_thr_act); - return(KERN_TERMINATED); + thread = act_lock_thread(target); + + if (!target->active) { + act_unlock_thread(target); + return (KERN_TERMINATED); } - thread_hold(target_thr_act); - while (1) { - if (!thread || target_thr_act != thread->top_act) + thread_hold(target); + + for (;;) { + thread_t thread1; + + if ( thread == THREAD_NULL || + thread->top_act != target ) break; - act_unlock_thread(target_thr_act); - (void)thread_stop_wait(thread); - nthread = act_lock_thread(target_thr_act); - if (nthread == thread) + act_unlock_thread(target); + + if (!thread_stop(thread)) { + result = KERN_ABORTED; + (void)act_lock_thread(target); + thread = THREAD_NULL; break; + } + + thread1 = act_lock_thread(target); + if (thread1 == thread) + break; + thread_unstop(thread); - thread = nthread; + thread = thread1; } - ret = act_thread_dup(source_thr_act, target_thr_act); - if (thread && target_thr_act == thread->top_act) + + if (result == KERN_SUCCESS) + result = act_thread_dup(self, target); + + if ( thread != THREAD_NULL && + thread->top_act == target ) thread_unstop(thread); - thread_release(target_thr_act); - act_unlock_thread(target_thr_act); - return(ret); + thread_release(target); + act_unlock_thread(target); + + return (result); } @@ -891,20 +794,29 @@ thread_dup( */ kern_return_t thread_setstatus( - thread_act_t thr_act, - int flavor, - thread_state_t tstate, + register thread_act_t act, + int flavor, + thread_state_t tstate, mach_msg_type_number_t count) { - kern_return_t kr; - thread_t thread; + kern_return_t result = KERN_SUCCESS; + thread_t thread; - thread = act_lock_thread(thr_act); - assert(thread); - assert(thread->top_act == thr_act); - kr = act_machine_set_state(thr_act, flavor, tstate, count); - act_unlock_thread(thr_act); - return(kr); + thread = act_lock_thread(act); + + if ( act != current_act() && + (act->suspend_count == 0 || + thread == THREAD_NULL || + (thread->state & TH_RUN) || + thread->top_act != act) ) + result = KERN_FAILURE; + + if (result == KERN_SUCCESS) + result = act_machine_set_state(act, flavor, tstate, count); + + act_unlock_thread(act); + + return (result); } /* @@ -914,20 +826,29 @@ thread_setstatus( */ kern_return_t thread_getstatus( - thread_act_t thr_act, - int flavor, - thread_state_t tstate, + register thread_act_t act, + int flavor, + thread_state_t tstate, mach_msg_type_number_t *count) { - kern_return_t kr; - thread_t thread; + kern_return_t result = KERN_SUCCESS; + thread_t thread; - thread = act_lock_thread(thr_act); - assert(thread); - assert(thread->top_act == thr_act); - kr = act_machine_get_state(thr_act, flavor, tstate, count); - act_unlock_thread(thr_act); - return(kr); + thread = act_lock_thread(act); + + if ( act != current_act() && + (act->suspend_count == 0 || + thread == THREAD_NULL || + (thread->state & TH_RUN) || + thread->top_act != act) ) + result = KERN_FAILURE; + + if (result == KERN_SUCCESS) + result = act_machine_get_state(act, flavor, tstate, count); + + act_unlock_thread(act); + + return (result); } /* @@ -945,6 +866,7 @@ act_init() ACT_MAX * sizeof(struct thread_activation), /* XXX */ ACT_CHUNK * sizeof(struct thread_activation), "activations"); + first_act = TRUE; act_machine_init(); } @@ -960,7 +882,11 @@ act_create(task_t task, int rc; vm_map_t map; - thr_act = (thread_act_t)zalloc(thr_act_zone); + if (first_act) { + thr_act = &pageout_act; + first_act = FALSE; + } else + thr_act = (thread_act_t)zalloc(thr_act_zone); if (thr_act == 0) return(KERN_RESOURCE_SHORTAGE); @@ -973,6 +899,9 @@ act_create(task_t task, /* Start by zeroing everything; then init non-zero items only */ bzero((char *)thr_act, sizeof(*thr_act)); + if (thr_act == &pageout_act) + thr_act->thread = &pageout_thread; + #ifdef MACH_BSD { /* @@ -981,8 +910,9 @@ act_create(task_t task, * handling trivial * uthread_alloc() will bzero the storage allocated. */ - extern void *uthread_alloc(void); - thr_act->uthread = uthread_alloc(); + extern void *uthread_alloc(task_t, thread_act_t); + + thr_act->uthread = uthread_alloc(task, thr_act); if(thr_act->uthread == 0) { /* Put the thr_act back on the thr_act zone */ zfree(thr_act_zone, (vm_offset_t)thr_act); @@ -1002,17 +932,6 @@ act_create(task_t task, thr_act->task = task; task_reference(task); - /* Initialize sigbufp for High-Watermark buffer allocation */ - thr_act->r_sigbufp = (routine_descriptor_t) &thr_act->r_sigbuf; - thr_act->r_sigbuf_size = sizeof(thr_act->r_sigbuf); - -#if THREAD_SWAPPER - thr_act->swap_state = TH_SW_IN; -#if MACH_ASSERT - thr_act->kernel_stack_swapped_in = TRUE; -#endif /* MACH_ASSERT */ -#endif /* THREAD_SWAPPER */ - /* special_handler will always be last on the returnhandlers list. */ thr_act->special_handler.next = 0; thr_act->special_handler.handler = special_handler; @@ -1046,10 +965,6 @@ act_create(task_t task, /* Inline vm_map_reference cause we don't want to increment res_count */ mutex_lock(&map->s_lock); -#if TASK_SWAPPER - assert(map->res_count > 0); - assert(map->ref_count >= map->res_count); -#endif /* TASK_SWAPPER */ map->ref_count++; mutex_unlock(&map->s_lock); @@ -1078,28 +993,23 @@ act_free(thread_act_t thr_act) thread_t thr; vm_map_t map; unsigned int ref; + void * task_proc; #if MACH_ASSERT if (watchacts & WA_EXIT) - printf("act_free(%x(%d)) thr=%x tsk=%x(%d) pport=%x%sactive\n", + printf("act_free(%x(%d)) thr=%x tsk=%x(%d) %sactive\n", thr_act, thr_act->ref_count, thr_act->thread, thr_act->task, thr_act->task ? thr_act->task->ref_count : 0, - thr_act->pool_port, thr_act->active ? " " : " !"); #endif /* MACH_ASSERT */ - -#if THREAD_SWAPPER - assert(thr_act->kernel_stack_swapped_in); -#endif /* THREAD_SWAPPER */ - assert(!thr_act->active); - assert(!thr_act->pool_port); task = thr_act->task; task_lock(task); + task_proc = task->bsd_info; if (thr = thr_act->thread) { time_value_t user_time, system_time; @@ -1114,15 +1024,6 @@ act_free(thread_act_t thr_act) queue_remove(&task->thr_acts, thr_act, thread_act_t, thr_acts); thr_act->thr_acts.next = NULL; task->thr_act_count--; - -#if THREAD_SWAPPER - /* - * Thread is supposed to be unswappable by now... - */ - assert(thr_act->swap_state == TH_SW_UNSWAPPABLE || - !thread_swap_unwire_stack); -#endif /* THREAD_SWAPPER */ - task->res_act_count--; task_unlock(task); task_deallocate(task); @@ -1138,7 +1039,6 @@ act_free(thread_act_t thr_act) task_deallocate(task); } - sigbuf_dealloc(thr_act); act_prof_deallocate(thr_act); ipc_thr_act_terminate(thr_act); @@ -1149,10 +1049,6 @@ act_free(thread_act_t thr_act) */ map = thr_act->map; mutex_lock(&map->s_lock); -#if TASK_SWAPPER - assert(map->res_count >= 0); - assert(map->ref_count > map->res_count); -#endif /* TASK_SWAPPER */ ref = --map->ref_count; mutex_unlock(&map->s_lock); if (ref == 0) @@ -1164,10 +1060,11 @@ act_free(thread_act_t thr_act) * Free uthread BEFORE the bzero. * Not doing so will result in a leak. */ - extern void uthread_free(void *); + extern void uthread_free(task_t, void *, void *); + void *ut = thr_act->uthread; thr_act->uthread = 0; - uthread_free(ut); + uthread_free(task, ut, task_proc); } #endif /* MACH_BSD */ @@ -1184,9 +1081,7 @@ act_free(thread_act_t thr_act) * act_attach - Attach an thr_act to the top of a thread ("push the stack"). * * The thread_shuttle must be either the current one or a brand-new one. - * Assumes the thr_act is active but not in use, also, that if it is - * attached to an thread_pool (i.e. the thread_pool pointer is nonzero), - * the thr_act has already been taken off the thread_pool's list. + * Assumes the thr_act is active but not in use. * * Already locked: thr_act plus "appropriate" thread-related locks * (see act_lock_thread()). @@ -1254,8 +1149,6 @@ act_detach( cur_act->ref_count--; assert(cur_act->ref_count > 0); - thread_pool_put_act(cur_act); - #if MACH_ASSERT cur_act->lower = cur_act->higher = THR_ACT_NULL; if (cur_thread->top_act) @@ -1267,65 +1160,39 @@ act_detach( /* - * Synchronize a thread operation with RPC. Called with nothing - * locked. Returns with thr_act locked, plus one of four - * combinations of other locks held: - * none - for new activation not yet associated with thread_pool - * or shuttle - * rpc_lock(thr_act->thread) only - for base activation (one - * without pool_port) - * ip_lock(thr_act->pool_port) only - for empty activation (one - * with no associated shuttle) - * both locks - for "active" activation (has shuttle, lives - * on thread_pool) - * If thr_act has an associated shuttle, this function returns - * its address. Otherwise it returns zero. + * Synchronize a thread operation with migration. + * Called with nothing locked. + * Returns with thr_act locked. */ thread_t act_lock_thread( thread_act_t thr_act) { - ipc_port_t pport; /* - * Allow the shuttle cloning code (q.v., when it - * exists :-}) to obtain ip_lock()'s while holding - * an rpc_lock(). + * JMM - We have moved away from explicit RPC locks + * and towards a generic migration approach. The wait + * queue lock will be the point of synchronization for + * the shuttle linkage when this is rolled out. Until + * then, just lock the act. */ - while (1) { - act_lock(thr_act); - pport = thr_act->pool_port; - if (!pport || ip_lock_try(pport)) { - if (!thr_act->thread) - break; - if (rpc_lock_try(thr_act->thread)) - break; - if (pport) - ip_unlock(pport); - } - act_unlock(thr_act); - mutex_pause(); - } + act_lock(thr_act); return (thr_act->thread); } /* - * Unsynchronize with RPC (i.e., undo an act_lock_thread() call). + * Unsynchronize with migration (i.e., undo an act_lock_thread() call). * Called with thr_act locked, plus thread locks held that are * "correct" for thr_act's state. Returns with nothing locked. */ void act_unlock_thread(thread_act_t thr_act) { - if (thr_act->thread) - rpc_unlock(thr_act->thread); - if (thr_act->pool_port) - ip_unlock(thr_act->pool_port); act_unlock(thr_act); } /* - * Synchronize with RPC given a pointer to a shuttle (instead of an + * Synchronize with migration given a pointer to a shuttle (instead of an * activation). Called with nothing locked; returns with all * "appropriate" thread-related locks held (see act_lock_thread()). */ @@ -1336,19 +1203,10 @@ thread_lock_act( thread_act_t thr_act; while (1) { - rpc_lock(thread); thr_act = thread->top_act; if (!thr_act) break; if (!act_lock_try(thr_act)) { - rpc_unlock(thread); - mutex_pause(); - continue; - } - if (thr_act->pool_port && - !ip_lock_try(thr_act->pool_port)) { - rpc_unlock(thread); - act_unlock(thr_act); mutex_pause(); continue; } @@ -1358,9 +1216,8 @@ thread_lock_act( } /* - * Unsynchronize with RPC starting from a pointer to a shuttle. - * Called with RPC-related locks held that are appropriate to - * shuttle's state; any activation is also locked. + * Unsynchronize with an activation starting from a pointer to + * a shuttle. */ void thread_unlock_act( @@ -1369,11 +1226,8 @@ thread_unlock_act( thread_act_t thr_act; if (thr_act = thread->top_act) { - if (thr_act->pool_port) - ip_unlock(thr_act->pool_port); act_unlock(thr_act); } - rpc_unlock(thread); } /* @@ -1381,7 +1235,7 @@ thread_unlock_act( * * If a new activation is given, switch to it. If not, * switch to the lower activation (pop). Returns the old - * activation. This is for RPC support. + * activation. This is for migration support. */ thread_act_t switch_act( @@ -1412,12 +1266,7 @@ switch_act( } assert(new != THR_ACT_NULL); -#if THREAD_SWAPPER - assert(new->swap_state != TH_SW_OUT && - new->swap_state != TH_SW_COMING_IN); -#endif /* THREAD_SWAPPER */ - - assert(cpu_data[cpu].active_thread == thread); + assert(cpu_to_processor(cpu)->cpu_data->active_thread == thread); active_kloaded[cpu] = (new->kernel_loaded) ? new : 0; /* This is where all the work happens */ @@ -1459,12 +1308,9 @@ install_special_handler( #endif /* MACH_ASSERT */ spl = splsched(); - if (thread) - thread_lock(thread); + thread_lock(thread); install_special_handler_locked(thr_act); - act_set_apc(thr_act); - if (thread) - thread_unlock(thread); + thread_unlock(thread); splx(spl); } @@ -1477,221 +1323,61 @@ install_special_handler( */ void install_special_handler_locked( - thread_act_t thr_act) + thread_act_t act) { + thread_t thread = act->thread; ReturnHandler **rh; - thread_t thread = thr_act->thread; /* The work handler must always be the last ReturnHandler on the list, because it can do tricky things like detach the thr_act. */ - for (rh = &thr_act->handlers; *rh; rh = &(*rh)->next) - /* */ ; - if (rh != &thr_act->special_handler.next) { - *rh = &thr_act->special_handler; - } - if (thread && thr_act == thread->top_act) { + for (rh = &act->handlers; *rh; rh = &(*rh)->next) + continue; + if (rh != &act->special_handler.next) + *rh = &act->special_handler; + + if (act == thread->top_act) { /* * Temporarily undepress, so target has * a chance to do locking required to * block itself in special_handler(). */ - if (thread->depress_priority >= 0) { - thread->priority = thread->depress_priority; - - /* - * Use special value -2 to indicate need - * to redepress priority in special_handler - * as thread blocks - */ - thread->depress_priority = -2; - compute_priority(thread, FALSE); - } + if (thread->sched_mode & TH_MODE_ISDEPRESSED) + compute_priority(thread, TRUE); } - act_set_apc(thr_act); -} -/* - * JMM - - * These two routines will be enhanced over time to call the general handler registration - * mechanism used by special handlers and alerts. They are hack in for now to avoid - * having to export the gory details of ASTs to the BSD code right now. - */ -extern thread_apc_handler_t bsd_ast; + thread_ast_set(act, AST_APC); + if (act == current_act()) + ast_propagate(act->ast); + else { + processor_t processor = thread->last_processor; + + if ( processor != PROCESSOR_NULL && + processor->state == PROCESSOR_RUNNING && + processor->cpu_data->active_thread == thread ) + cause_ast_check(processor); + } +} kern_return_t thread_apc_set( - thread_act_t thr_act, - thread_apc_handler_t apc) + thread_act_t act, + thread_apc_handler_t apc) { + extern thread_apc_handler_t bsd_ast; + assert(apc == bsd_ast); - thread_ast_set(thr_act, AST_BSD); - if (thr_act == current_act()) - ast_propagate(thr_act->ast); - return KERN_SUCCESS; + return (KERN_FAILURE); } kern_return_t thread_apc_clear( - thread_act_t thr_act, - thread_apc_handler_t apc) + thread_act_t act, + thread_apc_handler_t apc) { - assert(apc == bsd_ast); - thread_ast_clear(thr_act, AST_BSD); - if (thr_act == current_act()) - ast_off(AST_BSD); - return KERN_SUCCESS; -} - -/* - * act_set_thread_pool - Assign an activation to a specific thread_pool. - * Fails if the activation is already assigned to another pool. - * If thread_pool == 0, we remove the thr_act from its thread_pool. - * - * Called the port containing thread_pool already locked. - * Returns the same way. - */ -kern_return_t act_set_thread_pool( - thread_act_t thr_act, - ipc_port_t pool_port) -{ - thread_pool_t thread_pool; + extern thread_apc_handler_t bsd_ast; -#if MACH_ASSERT - if (watchacts & WA_ACT_LNK) - printf("act_set_thread_pool: %x(%d) -> %x\n", - thr_act, thr_act->ref_count, thread_pool); -#endif /* MACH_ASSERT */ - - if (pool_port == 0) { - thread_act_t *lact; - - if (thr_act->pool_port == 0) - return KERN_SUCCESS; - thread_pool = &thr_act->pool_port->ip_thread_pool; - - for (lact = &thread_pool->thr_acts; *lact; - lact = &((*lact)->thread_pool_next)) { - if (thr_act == *lact) { - *lact = thr_act->thread_pool_next; - break; - } - } - act_lock(thr_act); - thr_act->pool_port = 0; - thr_act->thread_pool_next = 0; - act_unlock(thr_act); - act_deallocate(thr_act); - return KERN_SUCCESS; - } - if (thr_act->pool_port != pool_port) { - thread_pool = &pool_port->ip_thread_pool; - if (thr_act->pool_port != 0) { -#if MACH_ASSERT - if (watchacts & WA_ACT_LNK) - printf("act_set_thread_pool found %x!\n", - thr_act->pool_port); -#endif /* MACH_ASSERT */ - return(KERN_FAILURE); - } - act_lock(thr_act); - thr_act->pool_port = pool_port; - - /* The pool gets a ref to the activation -- have - * to inline operation because thr_act is already - * locked. - */ - act_locked_act_reference(thr_act); - - /* If it is available, - * add it to the thread_pool's available-activation list. - */ - if ((thr_act->thread == 0) && (thr_act->suspend_count == 0)) { - thr_act->thread_pool_next = thread_pool->thr_acts; - pool_port->ip_thread_pool.thr_acts = thr_act; - if (thread_pool->waiting) - thread_pool_wakeup(thread_pool); - } - act_unlock(thr_act); - } - - return KERN_SUCCESS; -} - -/* - * act_locked_act_set_thread_pool- Assign activation to a specific thread_pool. - * Fails if the activation is already assigned to another pool. - * If thread_pool == 0, we remove the thr_act from its thread_pool. - * - * Called the port containing thread_pool already locked. - * Also called with the thread activation locked. - * Returns the same way. - * - * This routine is the same as `act_set_thread_pool()' except that it does - * not call `act_deallocate(),' which unconditionally tries to obtain the - * thread activation lock. - */ -kern_return_t act_locked_act_set_thread_pool( - thread_act_t thr_act, - ipc_port_t pool_port) -{ - thread_pool_t thread_pool; - -#if MACH_ASSERT - if (watchacts & WA_ACT_LNK) - printf("act_set_thread_pool: %x(%d) -> %x\n", - thr_act, thr_act->ref_count, thread_pool); -#endif /* MACH_ASSERT */ - - if (pool_port == 0) { - thread_act_t *lact; - - if (thr_act->pool_port == 0) - return KERN_SUCCESS; - thread_pool = &thr_act->pool_port->ip_thread_pool; - - for (lact = &thread_pool->thr_acts; *lact; - lact = &((*lact)->thread_pool_next)) { - if (thr_act == *lact) { - *lact = thr_act->thread_pool_next; - break; - } - } - - thr_act->pool_port = 0; - thr_act->thread_pool_next = 0; - act_locked_act_deallocate(thr_act); - return KERN_SUCCESS; - } - if (thr_act->pool_port != pool_port) { - thread_pool = &pool_port->ip_thread_pool; - if (thr_act->pool_port != 0) { -#if MACH_ASSERT - if (watchacts & WA_ACT_LNK) - printf("act_set_thread_pool found %x!\n", - thr_act->pool_port); -#endif /* MACH_ASSERT */ - return(KERN_FAILURE); - } - thr_act->pool_port = pool_port; - - /* The pool gets a ref to the activation -- have - * to inline operation because thr_act is already - * locked. - */ - act_locked_act_reference(thr_act); - - /* If it is available, - * add it to the thread_pool's available-activation list. - */ - if ((thr_act->thread == 0) && (thr_act->suspend_count == 0)) { - thr_act->thread_pool_next = thread_pool->thr_acts; - pool_port->ip_thread_pool.thr_acts = thr_act; - if (thread_pool->waiting) - thread_pool_wakeup(thread_pool); - } - } - - return KERN_SUCCESS; + assert(apc == bsd_ast); + return (KERN_FAILURE); } /* @@ -1703,52 +1389,46 @@ kern_return_t act_locked_act_set_thread_pool( * * This is called by system-dependent code when it detects that * thr_act->handlers is non-null while returning into user mode. - * Activations linked onto an thread_pool always have null thr_act->handlers, - * so RPC entry paths need not check it. */ -void act_execute_returnhandlers( - void) +void +act_execute_returnhandlers(void) { - spl_t s; - thread_t thread; - thread_act_t thr_act = current_act(); + thread_act_t act = current_act(); #if MACH_ASSERT if (watchacts & WA_ACT_HDLR) - printf("execute_rtn_hdlrs: thr_act=%x\n", thr_act); + printf("execute_rtn_hdlrs: act=%x\n", act); #endif /* MACH_ASSERT */ - s = splsched(); - act_clr_apc(thr_act); + thread_ast_clear(act, AST_APC); spllo(); - while (1) { - ReturnHandler *rh; - /* Grab the next returnhandler */ - thread = act_lock_thread(thr_act); + for (;;) { + ReturnHandler *rh; + thread_t thread = act_lock_thread(act); + (void)splsched(); thread_lock(thread); - rh = thr_act->handlers; + rh = act->handlers; if (!rh) { thread_unlock(thread); - splx(s); - act_unlock_thread(thr_act); + spllo(); + act_unlock_thread(act); return; } - thr_act->handlers = rh->next; + act->handlers = rh->next; thread_unlock(thread); spllo(); - act_unlock_thread(thr_act); + act_unlock_thread(act); #if MACH_ASSERT if (watchacts & WA_ACT_HDLR) - printf( (rh == &thr_act->special_handler) ? - "\tspecial_handler\n" : "\thandler=%x\n", - rh->handler); + printf( (rh == &act->special_handler) ? + "\tspecial_handler\n" : "\thandler=%x\n", rh->handler); #endif /* MACH_ASSERT */ /* Execute it */ - (*rh->handler)(rh, thr_act); + (*rh->handler)(rh, act); } } @@ -1764,28 +1444,28 @@ void act_execute_returnhandlers( void special_handler_continue(void) { - thread_act_t cur_act = current_act(); - thread_t thread = cur_act->thread; - spl_t s; + thread_act_t self = current_act(); - if (cur_act->suspend_count) - install_special_handler(cur_act); + if (self->suspend_count > 0) + install_special_handler(self); else { - s = splsched(); + thread_t thread = self->thread; + spl_t s = splsched(); + thread_lock(thread); - if (thread->depress_priority == -2) { - /* - * We were temporarily undepressed by - * install_special_handler; restore priority - * depression. - */ - thread->depress_priority = thread->priority; - thread->priority = thread->sched_pri = DEPRESSPRI; + if (thread->sched_mode & TH_MODE_ISDEPRESSED) { + processor_t myprocessor = thread->last_processor; + + thread->sched_pri = DEPRESSPRI; + myprocessor->current_pri = thread->sched_pri; + thread->sched_mode &= ~TH_MODE_PREEMPT; } thread_unlock(thread); splx(s); } + thread_exception_return(); + /*NOTREACHED*/ } /* @@ -1795,28 +1475,16 @@ special_handler_continue(void) void special_handler( ReturnHandler *rh, - thread_act_t cur_act) + thread_act_t self) { - spl_t s; - thread_t lthread; - thread_t thread = act_lock_thread(cur_act); - unsigned alert_bits; - exception_data_type_t - codes[EXCEPTION_CODE_MAX]; - kern_return_t kr; - kern_return_t exc_kr; + thread_t thread = act_lock_thread(self); + spl_t s; assert(thread != THREAD_NULL); -#if MACH_ASSERT - if (watchacts & WA_ACT_HDLR) - printf("\t\tspecial_handler(thr_act=%x(%d))\n", cur_act, - (cur_act ? cur_act->ref_count : 0)); -#endif /* MACH_ASSERT */ s = splsched(); - thread_lock(thread); - thread->state &= ~TH_ABORT; /* clear any aborts */ + thread->state &= ~(TH_ABORT|TH_ABORT_SAFELY); /* clear any aborts */ thread_unlock(thread); splx(s); @@ -1824,144 +1492,29 @@ special_handler( * If someone has killed this invocation, * invoke the return path with a terminated exception. */ - if (!cur_act->active) { - act_unlock_thread(cur_act); + if (!self->active) { + act_unlock_thread(self); act_machine_return(KERN_TERMINATED); } -#ifdef CALLOUT_RPC_MODEL - /* - * JMM - We don't intend to support this RPC model in Darwin. - * We will support inheritance through chains of activations - * on shuttles, but it will be universal and not just for RPC. - * As such, each activation will always have a base shuttle. - * Our RPC model will probably even support the notion of - * alerts (thrown up the chain of activations to affect the - * work done on our behalf), but the unlinking of the shuttles - * will be completely difference because we will never have - * to clone them. - */ - - /* strip server terminated bit */ - alert_bits = cur_act->alerts & (~SERVER_TERMINATED); - - /* clear server terminated bit */ - cur_act->alerts &= ~SERVER_TERMINATED; - - if ( alert_bits ) { - /* - * currently necessary to coordinate with the exception - * code -fdr - */ - act_unlock_thread(cur_act); - - /* upcall exception/alert port */ - codes[0] = alert_bits; - - /* - * Exception makes a lot of assumptions. If there is no - * exception handler or the exception reply is broken, the - * thread will be terminated and exception will not return. If - * we decide we don't like that behavior, we need to check - * for the existence of an exception port before we call - * exception. - */ - exc_kr = exception( EXC_RPC_ALERT, codes, 1 ); - - /* clear the orphaned and time constraint indications */ - cur_act->alerts &= ~(ORPHANED | TIME_CONSTRAINT_UNSATISFIED); - - /* if this orphaned activation should be terminated... */ - if (exc_kr == KERN_RPC_TERMINATE_ORPHAN) { - /* - * ... terminate the activation - * - * This is done in two steps. First, the activation is - * disabled (prepared for termination); second, the - * `special_handler()' is executed again -- this time - * to terminate the activation. - * (`act_disable_task_locked()' arranges for the - * additional execution of the `special_handler().') - */ - -#if THREAD_SWAPPER - thread_swap_disable(cur_act); -#endif /* THREAD_SWAPPER */ - - /* acquire appropriate locks */ - task_lock(cur_act->task); - act_lock_thread(cur_act); - - /* detach the activation from its task */ - kr = act_disable_task_locked(cur_act); - assert( kr == KERN_SUCCESS ); - - /* release locks */ - task_unlock(cur_act->task); - } - else { - /* acquire activation lock again (released below) */ - act_lock_thread(cur_act); - s = splsched(); - thread_lock(thread); - if (thread->depress_priority == -2) { - /* - * We were temporarily undepressed by - * install_special_handler; restore priority - * depression. - */ - thread->depress_priority = thread->priority; - thread->priority = thread->sched_pri = DEPRESSPRI; - } - thread_unlock(thread); - splx(s); - } - } -#endif /* CALLOUT_RPC_MODEL */ - /* * If we're suspended, go to sleep and wait for someone to wake us up. */ - if (cur_act->suspend_count) { - if( cur_act->handlers == NULL ) { - assert_wait((event_t)&cur_act->suspend_count, - THREAD_ABORTSAFE); - act_unlock_thread(cur_act); + if (self->suspend_count > 0) { + if (self->handlers == NULL) { + assert_wait(&self->suspend_count, THREAD_ABORTSAFE); + act_unlock_thread(self); thread_block(special_handler_continue); /* NOTREACHED */ } - special_handler_continue(); - } - act_unlock_thread(cur_act); -} - -/* - * Try to nudge a thr_act into executing its returnhandler chain. - * Ensures that the activation will execute its returnhandlers - * before it next executes any of its user-level code. - * - * Called with thr_act's act_lock() and "appropriate" thread-related - * locks held. (See act_lock_thread().) Returns same way. - */ -void -nudge(thread_act_t thr_act) -{ -#if MACH_ASSERT - if (watchacts & WA_ACT_HDLR) - printf("\tact_%x: nudge(%x)\n", current_act(), thr_act); -#endif /* MACH_ASSERT */ + act_unlock_thread(self); - /* - * Don't need to do anything at all if this thr_act isn't the topmost. - */ - if (thr_act->thread && thr_act->thread->top_act == thr_act) { - /* - * If it's suspended, wake it up. - * This should nudge it even on another CPU. - */ - thread_wakeup((event_t)&thr_act->suspend_count); + special_handler_continue(); + /*NOTREACHED*/ } + + act_unlock_thread(self); } /* @@ -1976,52 +1529,27 @@ act_user_to_kernel( } /* - * Already locked: thr_act->task, RPC-related locks for thr_act + * Already locked: activation (shuttle frozen within) * - * Detach an activation from its task, and prepare it to terminate + * Mark an activation inactive, and prepare it to terminate * itself. */ -kern_return_t -act_disable_task_locked( +static void +act_disable( thread_act_t thr_act) { - thread_t thread = thr_act->thread; - task_t task = thr_act->task; #if MACH_ASSERT if (watchacts & WA_EXIT) { - printf("act_%x: act_disable_tl(thr_act=%x(%d))%sactive task=%x(%d)", + printf("act_%x: act_disable_tl(thr_act=%x(%d))%sactive", current_act(), thr_act, thr_act->ref_count, - (thr_act->active ? " " : " !"), - thr_act->task, thr_act->task? thr_act->task->ref_count : 0); - if (thr_act->pool_port) - printf(", pool_port %x", thr_act->pool_port); + (thr_act->active ? " " : " !")); printf("\n"); (void) dump_act(thr_act); } #endif /* MACH_ASSERT */ - /* This will allow no more control ops on this thr_act. */ thr_act->active = 0; - ipc_thr_act_disable(thr_act); - - /* Clean-up any ulocks that are still owned by the thread - * activation (acquired but not released or handed-off). - */ - act_ulock_release_all(thr_act); - - /* When the special_handler gets executed, - * it will see the terminated condition and exit - * immediately. - */ - install_special_handler(thr_act); - - - /* If the target happens to be suspended, - * give it a nudge so it can exit. - */ - if (thr_act->suspend_count) - nudge(thr_act); /* Drop the thr_act reference taken for being active. * (There is still at least one reference left: @@ -2029,8 +1557,6 @@ act_disable_task_locked( * Inline the deallocate because thr_act is locked. */ act_locked_act_deallocate(thr_act); - - return(KERN_SUCCESS); } /* @@ -2096,11 +1622,16 @@ void set_state_handler(ReturnHandler *rh, thread_act_t thr_act); * thread-related locks held. (See act_lock_thread().) */ kern_return_t -get_set_state(thread_act_t thr_act, int flavor, thread_state_t state, int *pcount, - void (*handler)(ReturnHandler *rh, thread_act_t thr_act)) +get_set_state( + thread_act_t act, + int flavor, + thread_state_t state, + int *pcount, + void (*handler)( + ReturnHandler *rh, + thread_act_t act)) { - GetSetState gss; - spl_t s; + GetSetState gss; /* Initialize a small parameter structure */ gss.rh.handler = handler; @@ -2110,17 +1641,15 @@ get_set_state(thread_act_t thr_act, int flavor, thread_state_t state, int *pcoun gss.result = KERN_ABORTED; /* iff wait below is interrupted */ /* Add it to the thr_act's return handler list */ - gss.rh.next = thr_act->handlers; - thr_act->handlers = &gss.rh; + gss.rh.next = act->handlers; + act->handlers = &gss.rh; - s = splsched(); - act_set_apc(thr_act); - splx(s); + act_set_apc(act); #if MACH_ASSERT if (watchacts & WA_ACT_HDLR) { - printf("act_%x: get_set_state(thr_act=%x flv=%x state=%x ptr@%x=%x)", - current_act(), thr_act, flavor, state, + printf("act_%x: get_set_state(act=%x flv=%x state=%x ptr@%x=%x)", + current_act(), act, flavor, state, pcount, (pcount ? *pcount : 0)); printf((handler == get_state_handler ? "get_state_hdlr\n" : (handler == set_state_handler ? "set_state_hdlr\n" : @@ -2128,23 +1657,39 @@ get_set_state(thread_act_t thr_act, int flavor, thread_state_t state, int *pcoun } #endif /* MACH_ASSERT */ - assert(thr_act->thread); /* Callers must ensure these */ - assert(thr_act != current_act()); + assert(act->thread); + assert(act != current_act()); + for (;;) { - nudge(thr_act); + wait_result_t result; + + if ( act->inited && + act->thread->top_act == act ) + thread_wakeup_one(&act->suspend_count); + /* * Wait must be interruptible to avoid deadlock (e.g.) with * task_suspend() when caller and target of get_set_state() * are in same task. */ - assert_wait((event_t)&gss, THREAD_ABORTSAFE); - act_unlock_thread(thr_act); - thread_block((void (*)(void))0); - if (gss.result != KERN_ABORTED) + result = assert_wait(&gss, THREAD_ABORTSAFE); + act_unlock_thread(act); + + if (result == THREAD_WAITING) + result = thread_block(THREAD_CONTINUE_NULL); + + assert(result != THREAD_WAITING); + + if (gss.result != KERN_ABORTED) { + assert(result != THREAD_INTERRUPTED); break; + } + + /* JMM - What about other aborts (like BSD signals)? */ if (current_act()->handlers) act_execute_returnhandlers(); - act_lock_thread(thr_act); + + act_lock_thread(act); } #if MACH_ASSERT @@ -2153,7 +1698,7 @@ get_set_state(thread_act_t thr_act, int flavor, thread_state_t state, int *pcoun current_act(), gss.result); #endif /* MACH_ASSERT */ - return gss.result; + return (gss.result); } void @@ -2239,25 +1784,58 @@ act_get_state(thread_act_t thr_act, int flavor, thread_state_t state, return(act_get_state_locked(thr_act, flavor, state, pcount)); } -/* - * These two should be called at splsched() - * Set/clear indicator to run APC (layered on ASTs) - */ void -act_set_apc(thread_act_t thr_act) +act_set_astbsd( + thread_act_t act) { - thread_ast_set(thr_act, AST_APC); - if (thr_act == current_act()) { - mp_disable_preemption(); - ast_propagate(thr_act->ast); - mp_enable_preemption(); + spl_t s = splsched(); + + if (act == current_act()) { + thread_ast_set(act, AST_BSD); + ast_propagate(act->ast); } + else { + thread_t thread = act->thread; + processor_t processor; + + thread_lock(thread); + thread_ast_set(act, AST_BSD); + processor = thread->last_processor; + if ( processor != PROCESSOR_NULL && + processor->state == PROCESSOR_RUNNING && + processor->cpu_data->active_thread == thread ) + cause_ast_check(processor); + thread_unlock(thread); + } + + splx(s); } void -act_clr_apc(thread_act_t thr_act) +act_set_apc( + thread_act_t act) { - thread_ast_clear(thr_act, AST_APC); + spl_t s = splsched(); + + if (act == current_act()) { + thread_ast_set(act, AST_APC); + ast_propagate(act->ast); + } + else { + thread_t thread = act->thread; + processor_t processor; + + thread_lock(thread); + thread_ast_set(act, AST_APC); + processor = thread->last_processor; + if ( processor != PROCESSOR_NULL && + processor->state == PROCESSOR_RUNNING && + processor->cpu_data->active_thread == thread ) + cause_ast_check(processor); + thread_unlock(thread); + } + + splx(s); } void @@ -2276,13 +1854,6 @@ act_ulock_release_all(thread_act_t thr_act) * Provide routines (for export to other components) of things that * are implemented as macros insternally. */ -#undef current_act -thread_act_t -current_act(void) -{ - return(current_act_fast()); -} - thread_act_t thread_self(void) { @@ -2316,4 +1887,3 @@ act_deallocate( { act_deallocate_fast(thr_act); } -