Source to kern/syscall_subr.c
/*
* Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
* Reserved. 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 1.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.apple.com/publicsource 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."
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* Mach Operating System
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
#include <mach_fixpri.h>
#include <cpus.h>
#include <mach/boolean.h>
#include <mach/thread_switch.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_space.h>
#include <kern/counters.h>
#include <kern/ipc_kobject.h>
#include <kern/processor.h>
#include <kern/sched.h>
#include <kern/sched_prim.h>
#include <kern/ipc_sched.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/time_out.h>
#include <machine/machspl.h> /* for splsched */
#if MACH_FIXPRI
#include <mach/policy.h>
#endif MACH_FIXPRI
/*
* swtch and swtch_pri both attempt to context switch (logic in
* thread_block no-ops the context switch if nothing would happen).
* A boolean is returned that indicates whether there is anything
* else runnable.
*
* This boolean can be used by a thread waiting on a
* lock or condition: If FALSE is returned, the thread is justified
* in becoming a resource hog by continuing to spin because there's
* nothing else useful that the processor could do. If TRUE is
* returned, the thread should make one more check on the
* lock and then be a good citizen and really suspend.
*/
extern void thread_depress_priority();
extern kern_return_t thread_depress_abort();
void swtch_continue()
{
register processor_t myprocessor;
myprocessor = current_processor();
thread_syscall_return(myprocessor->runq.count > 0 ||
myprocessor->processor_set->runq.count > 0);
/*NOTREACHED*/
}
boolean_t swtch()
{
register processor_t myprocessor;
#if NCPUS > 1
myprocessor = current_processor();
if (myprocessor->runq.count == 0 &&
myprocessor->processor_set->runq.count == 0)
return(FALSE);
#endif NCPUS > 1
counter(c_swtch_block++);
thread_block_with_continuation(swtch_continue);
myprocessor = current_processor();
return(myprocessor->runq.count > 0 ||
myprocessor->processor_set->runq.count > 0);
}
void swtch_pri_continue()
{
register thread_t thread = current_thread();
register processor_t myprocessor;
if (thread->depress_priority >= 0)
(void) thread_depress_abort(thread);
myprocessor = current_processor();
thread_syscall_return(myprocessor->runq.count > 0 ||
myprocessor->processor_set->runq.count > 0);
/*NOTREACHED*/
}
boolean_t swtch_pri(pri)
int pri;
{
register thread_t thread = current_thread();
register processor_t myprocessor;
#ifdef lint
pri++;
#endif lint
#if NCPUS > 1
myprocessor = current_processor();
if (myprocessor->runq.count == 0 &&
myprocessor->processor_set->runq.count == 0)
return(FALSE);
#endif NCPUS > 1
/*
* XXX need to think about depression duration.
* XXX currently using min quantum.
*/
thread_depress_priority(thread, min_quantum);
counter(c_swtch_pri_block++);
thread_block_with_continuation(swtch_pri_continue);
if (thread->depress_priority >= 0)
(void) thread_depress_abort(thread);
myprocessor = current_processor();
return(myprocessor->runq.count > 0 ||
myprocessor->processor_set->runq.count > 0);
}
extern int hz;
void thread_switch_continue()
{
register thread_t cur_thread = current_thread();
/*
* Restore depressed priority
*/
if (cur_thread->depress_priority >= 0)
(void) thread_depress_abort(cur_thread);
thread_syscall_return(KERN_SUCCESS);
/*NOTREACHED*/
}
/*
* thread_switch:
*
* Context switch. User may supply thread hint.
*
* Fixed priority threads that call this get what they asked for
* even if that violates priority order.
*/
kern_return_t thread_switch(thread_name, option, option_time)
mach_port_t thread_name;
int option;
mach_msg_timeout_t option_time;
{
register thread_t cur_thread = current_thread();
register processor_t myprocessor;
kern_return_t result;
ipc_port_t port;
/*
* Process option.
*/
switch (option) {
case SWITCH_OPTION_NONE:
/*
* Nothing to do.
*/
break;
case SWITCH_OPTION_DEPRESS:
/*
* Depress priority for given time.
*/
thread_depress_priority(cur_thread, option_time);
break;
case SWITCH_OPTION_WAIT:
thread_will_wait_with_timeout(cur_thread, option_time);
break;
default:
return(KERN_INVALID_ARGUMENT);
}
/*
* Check and act on thread hint if appropriate.
*/
if ((thread_name != 0) &&
((result = ipc_port_translate_send(cur_thread->task->itk_space,
thread_name, &port)) == KERN_SUCCESS)) {
/* port is locked, but it might not be active */
/*
* Get corresponding thread.
*/
if (ip_active(port) && (ip_kotype(port) == IKOT_THREAD)) {
register thread_t thread;
register spl_t s;
thread = (thread_t) port->ip_kobject;
/*
* Check if the thread is in the right pset. Then
* pull it off its run queue. If it
* doesn't come, then it's not eligible.
*/
s = splsched();
thread_lock(thread);
if ((thread->processor_set == cur_thread->processor_set)
&& (rem_runq(thread) != RUN_QUEUE_NULL)) {
/*
* Hah, got it!!
*/
thread_unlock(thread);
(void) splx(s);
ip_unlock(port);
/* XXX thread might disappear on us now? */
#if MACH_FIXPRI
if (thread->policy == POLICY_FIXEDPRI) {
myprocessor = current_processor();
myprocessor->quantum = thread->sched_data;
myprocessor->first_quantum = TRUE;
}
#endif MACH_FIXPRI
counter(c_thread_switch_handoff++);
thread_run(thread_switch_continue, thread);
/*
* Restore depressed priority
*/
if (cur_thread->depress_priority >= 0)
(void) thread_depress_abort(cur_thread);
return(KERN_SUCCESS);
}
thread_unlock(thread);
(void) splx(s);
}
ip_unlock(port);
}
else if (thread_name != 0 && result == KERN_INVALID_NAME)
return (KERN_INVALID_ARGUMENT);
/*
* No handoff hint supplied, or hint was wrong. Call thread_block() in
* hopes of running something else. If nothing else is runnable,
* thread_block will detect this. WARNING: thread_switch with no
* option will not do anything useful if the thread calling it is the
* highest priority thread (can easily happen with a collection
* of timesharing threads).
*/
#if NCPUS > 1
myprocessor = current_processor();
if (myprocessor->processor_set->runq.count > 0 ||
myprocessor->runq.count > 0)
#endif NCPUS > 1
thread_block_with_continuation(thread_switch_continue);
/*
* Restore depressed priority
*/
if (cur_thread->depress_priority >= 0)
(void) thread_depress_abort(cur_thread);
return(KERN_SUCCESS);
}
/*
* thread_depress_priority
*
* Depress thread's priority to lowest possible for specified period.
* Intended for use when thread wants a lock but doesn't know which
* other thread is holding it. As with thread_switch, fixed
* priority threads get exactly what they asked for. Users access
* this by the SWITCH_OPTION_DEPRESS option to thread_switch. A Time
* of zero will result in no timeout being scheduled.
*/
void
thread_depress_priority(thread, depress_time)
register thread_t thread;
mach_msg_timeout_t depress_time;
{
unsigned int ticks;
spl_t s;
/* convert from milliseconds to ticks */
ticks = convert_ipc_timeout_to_ticks(depress_time);
s = splsched();
thread_lock(thread);
/*
* If thread is already depressed, override previous depression.
*/
reset_timeout_check(&thread->depress_timer);
/*
* Save current priority, then set priority and
* sched_pri to their lowest possible values.
*/
thread->depress_priority = thread->priority;
thread->priority = 0;
thread->sched_pri = 0;
if (ticks != 0)
set_timeout(&thread->depress_timer, ticks);
thread_unlock(thread);
(void) splx(s);
}
/*
* thread_depress_timeout:
*
* Timeout routine for priority depression.
*/
void
thread_depress_timeout(thread)
register thread_t thread;
{
spl_t s;
s = splsched();
thread_lock(thread);
/*
* If we lose a race with thread_depress_abort,
* then depress_priority might be -1.
*/
if (thread->depress_priority >= 0) {
thread->priority = thread->depress_priority;
thread->depress_priority = -1;
compute_priority(thread, FALSE);
}
thread_unlock(thread);
(void) splx(s);
}
/*
* thread_depress_abort:
*
* Prematurely abort priority depression if there is one.
*/
kern_return_t
thread_depress_abort(thread)
register thread_t thread;
{
spl_t s;
if (thread == THREAD_NULL)
return(KERN_INVALID_ARGUMENT);
s = splsched();
thread_lock(thread);
/*
* Only restore priority if thread is depressed.
*/
if (thread->depress_priority >= 0) {
reset_timeout_check(&thread->depress_timer);
thread->priority = thread->depress_priority;
thread->depress_priority = -1;
compute_priority(thread, FALSE);
}
thread_unlock(thread);
(void) splx(s);
return(KERN_SUCCESS);
}