std: Introduce a Runtime trait
This trait is used to abstract the differences between 1:1 and M:N scheduling and is the sole dispatch point for the differences between these two scheduling modes. This, and the following series of commits, is not intended to compile. Only after the entire transition is complete are programs expected to compile.
This commit is contained in:
parent
32e730f122
commit
cab44fb076
@ -185,41 +185,33 @@ pub mod args;
|
||||
// Support for dynamic borrowck
|
||||
pub mod borrowck;
|
||||
|
||||
/// Set up a default runtime configuration, given compiler-supplied arguments.
|
||||
///
|
||||
/// This is invoked by the `start` _language item_ (unstable::lang) to
|
||||
/// run a Rust executable.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `argc` & `argv` - The argument vector. On Unix this information is used
|
||||
/// by os::args.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The return value is used as the process return code. 0 on success, 101 on error.
|
||||
pub fn start(argc: int, argv: **u8, main: proc()) -> int {
|
||||
/// The default error code of the rust runtime if the main task fails instead
|
||||
/// of exiting cleanly.
|
||||
pub static DEFAULT_ERROR_CODE: int = 101;
|
||||
|
||||
init(argc, argv);
|
||||
let exit_code = run(main);
|
||||
// unsafe is ok b/c we're sure that the runtime is gone
|
||||
unsafe { cleanup(); }
|
||||
|
||||
return exit_code;
|
||||
}
|
||||
|
||||
/// Like `start` but creates an additional scheduler on the current thread,
|
||||
/// which in most cases will be the 'main' thread, and pins the main task to it.
|
||||
/// The interface to the current runtime.
|
||||
///
|
||||
/// This is appropriate for running code that must execute on the main thread,
|
||||
/// such as the platform event loop and GUI.
|
||||
pub fn start_on_main_thread(argc: int, argv: **u8, main: proc()) -> int {
|
||||
init(argc, argv);
|
||||
let exit_code = run_on_main_thread(main);
|
||||
// unsafe is ok b/c we're sure that the runtime is gone
|
||||
unsafe { cleanup(); }
|
||||
/// This trait is used as the abstraction between 1:1 and M:N scheduling. The
|
||||
/// two independent crates, libnative and libgreen, both have objects which
|
||||
/// implement this trait. The goal of this trait is to encompass all the
|
||||
/// fundamental differences in functionality between the 1:1 and M:N runtime
|
||||
/// modes.
|
||||
pub trait Runtime {
|
||||
// Necessary scheduling functions, used for channels and blocking I/O
|
||||
// (sometimes).
|
||||
fn yield_now(~self, cur_task: ~Task);
|
||||
fn maybe_yield(~self, cur_task: ~Task);
|
||||
fn deschedule(~self, times: uint, cur_task: ~Task,
|
||||
f: |BlockedTask| -> Result<(), BlockedTask>);
|
||||
fn reawaken(~self, to_wake: ~Task, can_resched: bool);
|
||||
|
||||
return exit_code;
|
||||
// Miscellaneous calls which are very different depending on what context
|
||||
// you're in.
|
||||
fn spawn_sibling(~self, cur_task: ~Task, opts: TaskOpts, f: proc());
|
||||
fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
|
||||
|
||||
// XXX: This is a serious code smell and this should not exist at all.
|
||||
fn wrap(~self) -> ~Any;
|
||||
}
|
||||
|
||||
/// One-time runtime initialization.
|
||||
@ -250,239 +242,3 @@ pub unsafe fn cleanup() {
|
||||
args::cleanup();
|
||||
local_ptr::cleanup();
|
||||
}
|
||||
|
||||
/// Execute the main function in a scheduler.
|
||||
///
|
||||
/// Configures the runtime according to the environment, by default
|
||||
/// using a task scheduler with the same number of threads as cores.
|
||||
/// Returns a process exit code.
|
||||
pub fn run(main: proc()) -> int {
|
||||
run_(main, false)
|
||||
}
|
||||
|
||||
pub fn run_on_main_thread(main: proc()) -> int {
|
||||
run_(main, true)
|
||||
}
|
||||
|
||||
fn run_(main: proc(), use_main_sched: bool) -> int {
|
||||
static DEFAULT_ERROR_CODE: int = 101;
|
||||
|
||||
let nscheds = util::default_sched_threads();
|
||||
|
||||
let mut main = Some(main);
|
||||
|
||||
// The shared list of sleeping schedulers.
|
||||
let sleepers = SleeperList::new();
|
||||
|
||||
// Create a work queue for each scheduler, ntimes. Create an extra
|
||||
// for the main thread if that flag is set. We won't steal from it.
|
||||
let mut pool = deque::BufferPool::new();
|
||||
let arr = vec::from_fn(nscheds, |_| pool.deque());
|
||||
let (workers, stealers) = vec::unzip(arr.move_iter());
|
||||
|
||||
// The schedulers.
|
||||
let mut scheds = ~[];
|
||||
// Handles to the schedulers. When the main task ends these will be
|
||||
// sent the Shutdown message to terminate the schedulers.
|
||||
let mut handles = ~[];
|
||||
|
||||
for worker in workers.move_iter() {
|
||||
rtdebug!("inserting a regular scheduler");
|
||||
|
||||
// Every scheduler is driven by an I/O event loop.
|
||||
let loop_ = new_event_loop();
|
||||
let mut sched = ~Scheduler::new(loop_,
|
||||
worker,
|
||||
stealers.clone(),
|
||||
sleepers.clone());
|
||||
let handle = sched.make_handle();
|
||||
|
||||
scheds.push(sched);
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// If we need a main-thread task then create a main thread scheduler
|
||||
// that will reject any task that isn't pinned to it
|
||||
let main_sched = if use_main_sched {
|
||||
|
||||
// Create a friend handle.
|
||||
let mut friend_sched = scheds.pop();
|
||||
let friend_handle = friend_sched.make_handle();
|
||||
scheds.push(friend_sched);
|
||||
|
||||
// This scheduler needs a queue that isn't part of the stealee
|
||||
// set.
|
||||
let (worker, _) = pool.deque();
|
||||
|
||||
let main_loop = new_event_loop();
|
||||
let mut main_sched = ~Scheduler::new_special(main_loop,
|
||||
worker,
|
||||
stealers.clone(),
|
||||
sleepers.clone(),
|
||||
false,
|
||||
Some(friend_handle));
|
||||
let mut main_handle = main_sched.make_handle();
|
||||
// Allow the scheduler to exit when the main task exits.
|
||||
// Note: sending the shutdown message also prevents the scheduler
|
||||
// from pushing itself to the sleeper list, which is used for
|
||||
// waking up schedulers for work stealing; since this is a
|
||||
// non-work-stealing scheduler it should not be adding itself
|
||||
// to the list.
|
||||
main_handle.send(Shutdown);
|
||||
Some(main_sched)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create a shared cell for transmitting the process exit
|
||||
// code from the main task to this function.
|
||||
let exit_code = UnsafeArc::new(AtomicInt::new(0));
|
||||
let exit_code_clone = exit_code.clone();
|
||||
|
||||
// Used to sanity check that the runtime only exits once
|
||||
let exited_already = UnsafeArc::new(AtomicBool::new(false));
|
||||
|
||||
// When the main task exits, after all the tasks in the main
|
||||
// task tree, shut down the schedulers and set the exit code.
|
||||
let handles = handles;
|
||||
let on_exit: proc(TaskResult) = proc(exit_success) {
|
||||
unsafe {
|
||||
assert!(!(*exited_already.get()).swap(true, SeqCst),
|
||||
"the runtime already exited");
|
||||
}
|
||||
|
||||
let mut handles = handles;
|
||||
for handle in handles.mut_iter() {
|
||||
handle.send(Shutdown);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let exit_code = if exit_success.is_ok() {
|
||||
use rt::util;
|
||||
|
||||
// If we're exiting successfully, then return the global
|
||||
// exit status, which can be set programmatically.
|
||||
util::get_exit_status()
|
||||
} else {
|
||||
DEFAULT_ERROR_CODE
|
||||
};
|
||||
(*exit_code_clone.get()).store(exit_code, SeqCst);
|
||||
}
|
||||
};
|
||||
|
||||
let mut threads = ~[];
|
||||
let mut on_exit = Some(on_exit);
|
||||
|
||||
if !use_main_sched {
|
||||
|
||||
// In the case where we do not use a main_thread scheduler we
|
||||
// run the main task in one of our threads.
|
||||
|
||||
let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool,
|
||||
None,
|
||||
::util::replace(&mut main,
|
||||
None).unwrap());
|
||||
main_task.name = Some(SendStrStatic("<main>"));
|
||||
main_task.death.on_exit = ::util::replace(&mut on_exit, None);
|
||||
|
||||
let sched = scheds.pop();
|
||||
let main_task = main_task;
|
||||
let thread = do Thread::start {
|
||||
sched.bootstrap(main_task);
|
||||
};
|
||||
threads.push(thread);
|
||||
}
|
||||
|
||||
// Run each remaining scheduler in a thread.
|
||||
for sched in scheds.move_rev_iter() {
|
||||
rtdebug!("creating regular schedulers");
|
||||
let thread = do Thread::start {
|
||||
let mut sched = sched;
|
||||
let bootstrap_task = ~do Task::new_root(&mut sched.stack_pool, None) || {
|
||||
rtdebug!("boostraping a non-primary scheduler");
|
||||
};
|
||||
sched.bootstrap(bootstrap_task);
|
||||
};
|
||||
threads.push(thread);
|
||||
}
|
||||
|
||||
// If we do have a main thread scheduler, run it now.
|
||||
|
||||
if use_main_sched {
|
||||
rtdebug!("about to create the main scheduler task");
|
||||
|
||||
let mut main_sched = main_sched.unwrap();
|
||||
|
||||
let home = Sched(main_sched.make_handle());
|
||||
let mut main_task = ~Task::new_root_homed(&mut main_sched.stack_pool,
|
||||
None,
|
||||
home,
|
||||
::util::replace(&mut main,
|
||||
None).
|
||||
unwrap());
|
||||
main_task.name = Some(SendStrStatic("<main>"));
|
||||
main_task.death.on_exit = ::util::replace(&mut on_exit, None);
|
||||
rtdebug!("bootstrapping main_task");
|
||||
|
||||
main_sched.bootstrap(main_task);
|
||||
}
|
||||
|
||||
rtdebug!("waiting for threads");
|
||||
|
||||
// Wait for schedulers
|
||||
for thread in threads.move_iter() {
|
||||
thread.join();
|
||||
}
|
||||
|
||||
// Return the exit code
|
||||
unsafe {
|
||||
(*exit_code.get()).load(SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_sched_context() -> bool {
|
||||
unsafe {
|
||||
let task_ptr: Option<*mut Task> = Local::try_unsafe_borrow();
|
||||
match task_ptr {
|
||||
Some(task) => {
|
||||
match (*task).task_type {
|
||||
SchedTask => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn in_green_task_context() -> bool {
|
||||
unsafe {
|
||||
let task: Option<*mut Task> = Local::try_unsafe_borrow();
|
||||
match task {
|
||||
Some(task) => {
|
||||
match (*task).task_type {
|
||||
GreenTask(_) => true,
|
||||
_ => false
|
||||
}
|
||||
}
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_event_loop() -> ~rtio::EventLoop {
|
||||
match crate_map::get_crate_map() {
|
||||
None => {}
|
||||
Some(map) => {
|
||||
match map.event_loop_factory {
|
||||
None => {}
|
||||
Some(factory) => return factory()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the crate map didn't specify a factory to create an event loop, then
|
||||
// instead just use a basic event loop missing all I/O services to at least
|
||||
// get the scheduler running.
|
||||
return basic::event_loop();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user