// option. This file may not be copied, modified, or distributed
// except according to those terms.
-/*! The Rust Runtime, including the task scheduler and I/O
+/*! Runtime services, including the task scheduler and I/O dispatcher
The `rt` module provides the private runtime infrastructure necessary
to support core language features like the exchange and local heap,
// XXX: this should not be here.
#[allow(missing_doc)];
-use clone::Clone;
-use container::Container;
-use iter::Iterator;
-use option::{Option, None, Some};
-use ptr::RawPtr;
-use rt::local::Local;
-use rt::sched::{Scheduler, Shutdown};
-use rt::sleeper_list::SleeperList;
-use rt::task::UnwindResult;
-use rt::task::{Task, SchedTask, GreenTask, Sched};
-use send_str::SendStrStatic;
-use unstable::atomics::{AtomicInt, AtomicBool, SeqCst};
-use unstable::sync::UnsafeArc;
-use vec::{OwnedVector, MutableVector, ImmutableVector};
-use vec;
-
-use self::thread::Thread;
-
-// the os module needs to reach into this helper, so allow general access
-// through this reexport.
-pub use self::util::set_exit_status;
+use any::Any;
+use option::Option;
+use result::Result;
+use task::TaskOpts;
+
+use self::task::{Task, BlockedTask};
// this is somewhat useful when a program wants to spawn a "reasonable" number
// of workers based on the constraints of the system that it's running on.
// method...
pub use self::util::default_sched_threads;
-// Re-export of the functionality in the kill module
-pub use self::kill::BlockedTask;
+// Export unwinding facilities used by the failure macros
+pub use self::unwind::{begin_unwind, begin_unwind_raw};
// XXX: these probably shouldn't be public...
#[doc(hidden)]
pub mod shouldnt_be_public {
- pub use super::select::SelectInner;
- pub use super::select::{SelectInner, SelectPortInner};
pub use super::local_ptr::native::maybe_tls_key;
#[cfg(not(windows), not(target_os = "android"))]
pub use super::local_ptr::compiled::RT_TLS_PTR;
// Internal macros used by the runtime.
mod macros;
-/// Basic implementation of an EventLoop, provides no I/O interfaces
-mod basic;
-
/// The global (exchange) heap.
pub mod global_heap;
/// Implementations of language-critical runtime features like @.
pub mod task;
-/// Facilities related to task failure, killing, and death.
-mod kill;
-
-/// The coroutine task scheduler, built on the `io` event loop.
-pub mod sched;
-
/// The EventLoop and internal synchronous I/O interface.
pub mod rtio;
/// or task-local storage.
pub mod local;
-/// A parallel queue.
-pub mod message_queue;
-
-/// A mostly lock-free multi-producer, single consumer queue.
-mod mpsc_queue;
-
-/// A lock-free multi-producer, multi-consumer bounded queue.
-mod mpmc_bounded_queue;
-
-/// A parallel work-stealing deque
-pub mod deque;
-
-/// A parallel data structure for tracking sleeping schedulers.
-pub mod sleeper_list;
-
-/// Stack segments and caching.
-pub mod stack;
-
-/// CPU context swapping.
-mod context;
-
/// Bindings to system threading libraries.
pub mod thread;
/// Crate map
pub mod crate_map;
-/// Tools for testing the runtime
-pub mod test;
-
-/// Reference counting
-pub mod rc;
-
-/// A simple single-threaded channel type for passing buffered data between
-/// scheduler and task context
-pub mod tube;
-
-/// Simple reimplementation of std::comm
-pub mod comm;
-
-mod select;
-
/// The runtime needs to be able to put a pointer into thread-local storage.
mod local_ptr;
/// Bindings to pthread/windows thread-local storage.
mod thread_local_storage;
+/// Stack unwinding
+pub mod unwind;
+
/// Just stuff
mod util;
// 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 {
-
- init(argc, argv);
- let exit_code = run(main);
- // unsafe is ok b/c we're sure that the runtime is gone
- unsafe { cleanup(); }
+/// The default error code of the rust runtime if the main task fails instead
+/// of exiting cleanly.
+pub static DEFAULT_ERROR_CODE: int = 101;
- 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(); }
-
- return exit_code;
+/// 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);
+
+ // 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>>;
+ /// The (low, high) edges of the current stack.
+ fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
+
+ // XXX: This is a serious code smell and this should not exist at all.
+ fn wrap(~self) -> ~Any;
}
/// One-time runtime initialization.
args::init(argc, argv);
env::init();
logging::init();
+ local_ptr::init();
}
}
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(UnwindResult) = 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_success() {
- 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();
-}