/* * Mach Operating System * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University. * Copyright (c) 1993,1994 The University of Utah and * the Computer Systems Laboratory (CSL). * 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, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM 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 Software.Distribution@CS.CMU.EDU * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for splsched */ #if MACH_FIXPRI #include #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. */ void swtch_continue(void) { processor_t myprocessor; myprocessor = current_processor(); thread_syscall_return(myprocessor->runq.count > 0 || myprocessor->processor_set->runq.count > 0); /*NOTREACHED*/ } boolean_t swtch(void) { 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(swtch_continue); myprocessor = current_processor(); return(myprocessor->runq.count > 0 || myprocessor->processor_set->runq.count > 0); } void swtch_pri_continue(void) { thread_t thread = current_thread(); 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(int pri) { thread_t thread = current_thread(); 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 */ /* * 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(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); } void thread_switch_continue(void) { 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( mach_port_t thread_name, int option, mach_msg_timeout_t option_time) { thread_t cur_thread = current_thread(); processor_t myprocessor; 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); } #ifndef MIGRATING_THREADS /* XXX thread_run defunct */ /* * Check and act on thread hint if appropriate. */ if ((thread_name != 0) && (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)) { thread_t thread; 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); } #endif /* not MIGRATING_THREADS */ /* * 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 */ { counter(c_thread_switch_block++); thread_block(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_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 = NRQS-1; thread->sched_pri = NRQS-1; 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_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_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); } /* * mach_print * * Display a null-terminated character string on the Mach console. * This system call is meant as a debugging tool useful to circumvent * messaging altogether. */ #ifdef MACH_KDB void mach_print(const char *s) { printf("%s", s); } #endif /* MACH_KDB */