diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index df1ebeb6407a..be35e7579b78 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -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>; + + // 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_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_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(); -}