about summary refs log tree commit diff
path: root/src/libstd/rt
diff options
context:
space:
mode:
authorEric Reed <ereed@mozilla.com>2013-06-25 11:45:44 -0700
committerEric Reed <ereed@mozilla.com>2013-06-25 11:45:44 -0700
commit4870dce3ebfd0e988a2e45360c724ebe912c3ad5 (patch)
treec676e677bf36924cbf177b0723b4ffe0fc435d40 /src/libstd/rt
parent794923c99511398bc90400e380dd11770ec8e614 (diff)
parente65d0cbabebc73f2c9733a7ed158576c9702e71e (diff)
downloadrust-4870dce3ebfd0e988a2e45360c724ebe912c3ad5.tar.gz
rust-4870dce3ebfd0e988a2e45360c724ebe912c3ad5.zip
Merge remote-tracking branch 'upstream/io' into io
Conflicts:
	src/rt/rustrt.def.in
Diffstat (limited to 'src/libstd/rt')
-rw-r--r--src/libstd/rt/args.rs125
-rw-r--r--src/libstd/rt/borrowck.rs283
-rw-r--r--src/libstd/rt/comm.rs12
-rw-r--r--src/libstd/rt/context.rs2
-rw-r--r--src/libstd/rt/io/extensions.rs3
-rw-r--r--src/libstd/rt/local_heap.rs59
-rw-r--r--src/libstd/rt/logging.rs20
-rw-r--r--src/libstd/rt/mod.rs113
-rw-r--r--src/libstd/rt/sched.rs4
-rw-r--r--src/libstd/rt/task.rs58
-rw-r--r--src/libstd/rt/test.rs2
-rw-r--r--src/libstd/rt/util.rs12
-rw-r--r--src/libstd/rt/uv/timer.rs8
13 files changed, 641 insertions, 60 deletions
diff --git a/src/libstd/rt/args.rs b/src/libstd/rt/args.rs
new file mode 100644
index 00000000000..75ee4f381f6
--- /dev/null
+++ b/src/libstd/rt/args.rs
@@ -0,0 +1,125 @@
+// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Global storage for command line arguments
+//!
+//! The current incarnation of the Rust runtime expects for
+//! the processes `argc` and `argv` arguments to be stored
+//! in a globally-accessible location for use by the `os` module.
+//!
+//! XXX: Would be nice for this to not exist.
+//! XXX: This has a lot of C glue for lack of globals.
+
+use libc;
+use option::{Option, Some, None};
+use str;
+use uint;
+use unstable::finally::Finally;
+use util;
+
+/// One-time global initialization.
+pub unsafe fn init(argc: int, argv: **u8) {
+    let args = load_argc_and_argv(argc, argv);
+    put(args);
+}
+
+/// One-time global cleanup.
+pub fn cleanup() {
+    rtassert!(take().is_some());
+}
+
+/// Take the global arguments from global storage.
+pub fn take() -> Option<~[~str]> {
+    with_lock(|| unsafe {
+        let ptr = get_global_ptr();
+        let val = util::replace(&mut *ptr, None);
+        val.map(|s: &~~[~str]| (**s).clone())
+    })
+}
+
+/// Give the global arguments to global storage.
+///
+/// It is an error if the arguments already exist.
+pub fn put(args: ~[~str]) {
+    with_lock(|| unsafe {
+        let ptr = get_global_ptr();
+        rtassert!((*ptr).is_none());
+        (*ptr) = Some(~args.clone());
+    })
+}
+
+/// Make a clone of the global arguments.
+pub fn clone() -> Option<~[~str]> {
+    with_lock(|| unsafe {
+        let ptr = get_global_ptr();
+        (*ptr).map(|s: &~~[~str]| (**s).clone())
+    })
+}
+
+fn with_lock<T>(f: &fn() -> T) -> T {
+    do (|| {
+        unsafe {
+            rust_take_global_args_lock();
+            f()
+        }
+    }).finally {
+        unsafe {
+            rust_drop_global_args_lock();
+        }
+    }
+}
+
+fn get_global_ptr() -> *mut Option<~~[~str]> {
+    unsafe { rust_get_global_args_ptr() }
+}
+
+// Copied from `os`.
+unsafe fn load_argc_and_argv(argc: int, argv: **u8) -> ~[~str] {
+    let mut args = ~[];
+    for uint::range(0, argc as uint) |i| {
+        args.push(str::raw::from_c_str(*(argv as **libc::c_char).offset(i)));
+    }
+    return args;
+}
+
+extern {
+    fn rust_take_global_args_lock();
+    fn rust_drop_global_args_lock();
+    fn rust_get_global_args_ptr() -> *mut Option<~~[~str]>;
+}
+
+#[cfg(test)]
+mod tests {
+    use option::{Some, None};
+    use super::*;
+    use unstable::finally::Finally;
+
+    #[test]
+    fn smoke_test() {
+        // Preserve the actual global state.
+        let saved_value = take();
+
+        let expected = ~[~"happy", ~"today?"];
+
+        put(expected.clone());
+        assert!(clone() == Some(expected.clone()));
+        assert!(take() == Some(expected.clone()));
+        assert!(take() == None);
+
+        do (|| {
+        }).finally {
+            // Restore the actual global state.
+            match saved_value {
+                Some(ref args) => put(args.clone()),
+                None => ()
+            }
+        }
+    }
+}
diff --git a/src/libstd/rt/borrowck.rs b/src/libstd/rt/borrowck.rs
new file mode 100644
index 00000000000..e057f6e9637
--- /dev/null
+++ b/src/libstd/rt/borrowck.rs
@@ -0,0 +1,283 @@
+// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use cast::transmute;
+use libc::{c_char, c_void, size_t, STDERR_FILENO};
+use io;
+use io::{Writer, WriterUtil};
+use managed::raw::BoxRepr;
+use option::{Option, None, Some};
+use uint;
+use str;
+use str::OwnedStr;
+use sys;
+use vec::ImmutableVector;
+
+#[allow(non_camel_case_types)]
+type rust_task = c_void;
+
+pub static FROZEN_BIT: uint = 1 << (uint::bits - 1);
+pub static MUT_BIT: uint = 1 << (uint::bits - 2);
+static ALL_BITS: uint = FROZEN_BIT | MUT_BIT;
+
+#[deriving(Eq)]
+struct BorrowRecord {
+    box: *mut BoxRepr,
+    file: *c_char,
+    line: size_t
+}
+
+fn try_take_task_borrow_list() -> Option<~[BorrowRecord]> {
+    unsafe {
+        let cur_task: *rust_task = rust_try_get_task();
+        if cur_task.is_not_null() {
+            let ptr = rust_take_task_borrow_list(cur_task);
+            if ptr.is_null() {
+                None
+            } else {
+                let v: ~[BorrowRecord] = transmute(ptr);
+                Some(v)
+            }
+        } else {
+            None
+        }
+    }
+}
+
+fn swap_task_borrow_list(f: &fn(~[BorrowRecord]) -> ~[BorrowRecord]) {
+    unsafe {
+        let cur_task: *rust_task = rust_try_get_task();
+        if cur_task.is_not_null() {
+            let mut borrow_list: ~[BorrowRecord] = {
+                let ptr = rust_take_task_borrow_list(cur_task);
+                if ptr.is_null() { ~[] } else { transmute(ptr) }
+            };
+            borrow_list = f(borrow_list);
+            rust_set_task_borrow_list(cur_task, transmute(borrow_list));
+        }
+    }
+}
+
+pub unsafe fn clear_task_borrow_list() {
+    // pub because it is used by the box annihilator.
+    let _ = try_take_task_borrow_list();
+}
+
+unsafe fn fail_borrowed(box: *mut BoxRepr, file: *c_char, line: size_t) {
+    debug_borrow("fail_borrowed: ", box, 0, 0, file, line);
+
+    match try_take_task_borrow_list() {
+        None => { // not recording borrows
+            let msg = "borrowed";
+            do str::as_buf(msg) |msg_p, _| {
+                sys::begin_unwind_(msg_p as *c_char, file, line);
+            }
+        }
+        Some(borrow_list) => { // recording borrows
+            let mut msg = ~"borrowed";
+            let mut sep = " at ";
+            for borrow_list.rev_iter().advance |entry| {
+                if entry.box == box {
+                    msg.push_str(sep);
+                    let filename = str::raw::from_c_str(entry.file);
+                    msg.push_str(filename);
+                    msg.push_str(fmt!(":%u", entry.line as uint));
+                    sep = " and at ";
+                }
+            }
+            do str::as_buf(msg) |msg_p, _| {
+                sys::begin_unwind_(msg_p as *c_char, file, line)
+            }
+        }
+    }
+}
+
+/// Because this code is so perf. sensitive, use a static constant so that
+/// debug printouts are compiled out most of the time.
+static ENABLE_DEBUG: bool = false;
+
+#[inline]
+unsafe fn debug_borrow<T>(tag: &'static str,
+                          p: *const T,
+                          old_bits: uint,
+                          new_bits: uint,
+                          filename: *c_char,
+                          line: size_t) {
+    //! A useful debugging function that prints a pointer + tag + newline
+    //! without allocating memory.
+
+    if ENABLE_DEBUG && ::rt::env::get().debug_borrow {
+        debug_borrow_slow(tag, p, old_bits, new_bits, filename, line);
+    }
+
+    unsafe fn debug_borrow_slow<T>(tag: &'static str,
+                                   p: *const T,
+                                   old_bits: uint,
+                                   new_bits: uint,
+                                   filename: *c_char,
+                                   line: size_t) {
+        let dbg = STDERR_FILENO as io::fd_t;
+        dbg.write_str(tag);
+        dbg.write_hex(p as uint);
+        dbg.write_str(" ");
+        dbg.write_hex(old_bits);
+        dbg.write_str(" ");
+        dbg.write_hex(new_bits);
+        dbg.write_str(" ");
+        dbg.write_cstr(filename);
+        dbg.write_str(":");
+        dbg.write_hex(line as uint);
+        dbg.write_str("\n");
+    }
+}
+
+trait DebugPrints {
+    fn write_hex(&self, val: uint);
+    unsafe fn write_cstr(&self, str: *c_char);
+}
+
+impl DebugPrints for io::fd_t {
+    fn write_hex(&self, mut i: uint) {
+        let letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8',
+                       '9', 'a', 'b', 'c', 'd', 'e', 'f'];
+        static uint_nibbles: uint = ::uint::bytes << 1;
+        let mut buffer = [0_u8, ..uint_nibbles+1];
+        let mut c = uint_nibbles;
+        while c > 0 {
+            c -= 1;
+            buffer[c] = letters[i & 0xF] as u8;
+            i >>= 4;
+        }
+        self.write(buffer.slice(0, uint_nibbles));
+    }
+
+    unsafe fn write_cstr(&self, p: *c_char) {
+        use libc::strlen;
+        use vec;
+
+        let len = strlen(p);
+        let p: *u8 = transmute(p);
+        do vec::raw::buf_as_slice(p, len as uint) |s| {
+            self.write(s);
+        }
+    }
+}
+
+#[inline]
+pub unsafe fn borrow_as_imm(a: *u8, file: *c_char, line: size_t) -> uint {
+    let a: *mut BoxRepr = transmute(a);
+    let old_ref_count = (*a).header.ref_count;
+    let new_ref_count = old_ref_count | FROZEN_BIT;
+
+    debug_borrow("borrow_as_imm:", a, old_ref_count, new_ref_count, file, line);
+
+    if (old_ref_count & MUT_BIT) != 0 {
+        fail_borrowed(a, file, line);
+    }
+
+    (*a).header.ref_count = new_ref_count;
+
+    old_ref_count
+}
+
+#[inline]
+pub unsafe fn borrow_as_mut(a: *u8, file: *c_char, line: size_t) -> uint {
+    let a: *mut BoxRepr = transmute(a);
+    let old_ref_count = (*a).header.ref_count;
+    let new_ref_count = old_ref_count | MUT_BIT | FROZEN_BIT;
+
+    debug_borrow("borrow_as_mut:", a, old_ref_count, new_ref_count, file, line);
+
+    if (old_ref_count & (MUT_BIT|FROZEN_BIT)) != 0 {
+        fail_borrowed(a, file, line);
+    }
+
+    (*a).header.ref_count = new_ref_count;
+
+    old_ref_count
+}
+
+pub unsafe fn record_borrow(a: *u8, old_ref_count: uint,
+                            file: *c_char, line: size_t) {
+    if (old_ref_count & ALL_BITS) == 0 {
+        // was not borrowed before
+        let a: *mut BoxRepr = transmute(a);
+        debug_borrow("record_borrow:", a, old_ref_count, 0, file, line);
+        do swap_task_borrow_list |borrow_list| {
+            let mut borrow_list = borrow_list;
+            borrow_list.push(BorrowRecord {box: a, file: file, line: line});
+            borrow_list
+        }
+    }
+}
+
+pub unsafe fn unrecord_borrow(a: *u8, old_ref_count: uint,
+                              file: *c_char, line: size_t) {
+    if (old_ref_count & ALL_BITS) == 0 {
+        // was not borrowed before, so we should find the record at
+        // the end of the list
+        let a: *mut BoxRepr = transmute(a);
+        debug_borrow("unrecord_borrow:", a, old_ref_count, 0, file, line);
+        do swap_task_borrow_list |borrow_list| {
+            let mut borrow_list = borrow_list;
+            assert!(!borrow_list.is_empty());
+            let br = borrow_list.pop();
+            if br.box != a || br.file != file || br.line != line {
+                let err = fmt!("wrong borrow found, br=%?", br);
+                do str::as_buf(err) |msg_p, _| {
+                    sys::begin_unwind_(msg_p as *c_char, file, line)
+                }
+            }
+            borrow_list
+        }
+    }
+}
+
+#[inline]
+pub unsafe fn return_to_mut(a: *u8, orig_ref_count: uint,
+                            file: *c_char, line: size_t) {
+    // Sometimes the box is null, if it is conditionally frozen.
+    // See e.g. #4904.
+    if !a.is_null() {
+        let a: *mut BoxRepr = transmute(a);
+        let old_ref_count = (*a).header.ref_count;
+        let new_ref_count =
+            (old_ref_count & !ALL_BITS) | (orig_ref_count & ALL_BITS);
+
+        debug_borrow("return_to_mut:",
+                     a, old_ref_count, new_ref_count, file, line);
+
+        (*a).header.ref_count = new_ref_count;
+    }
+}
+
+#[inline]
+pub unsafe fn check_not_borrowed(a: *u8,
+                                 file: *c_char,
+                                 line: size_t) {
+    let a: *mut BoxRepr = transmute(a);
+    let ref_count = (*a).header.ref_count;
+    debug_borrow("check_not_borrowed:", a, ref_count, 0, file, line);
+    if (ref_count & FROZEN_BIT) != 0 {
+        fail_borrowed(a, file, line);
+    }
+}
+
+
+extern {
+    #[rust_stack]
+    pub fn rust_take_task_borrow_list(task: *rust_task) -> *c_void;
+
+    #[rust_stack]
+    pub fn rust_set_task_borrow_list(task: *rust_task, map: *c_void);
+
+    #[rust_stack]
+    pub fn rust_try_get_task() -> *rust_task;
+}
diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs
index 82e6d44fe62..dd27c03ff51 100644
--- a/src/libstd/rt/comm.rs
+++ b/src/libstd/rt/comm.rs
@@ -399,12 +399,6 @@ impl<T: Owned> GenericChan<T> for SharedChan<T> {
 }
 
 impl<T: Owned> GenericSmartChan<T> for SharedChan<T> {
-    #[cfg(stage0)] // odd type checking errors
-    fn try_send(&self, _val: T) -> bool {
-        fail!()
-    }
-
-    #[cfg(not(stage0))]
     fn try_send(&self, val: T) -> bool {
         unsafe {
             let (next_pone, next_cone) = oneshot();
@@ -448,12 +442,6 @@ impl<T: Owned> GenericPort<T> for SharedPort<T> {
         }
     }
 
-    #[cfg(stage0)] // odd type checking errors
-    fn try_recv(&self) -> Option<T> {
-        fail!()
-    }
-
-    #[cfg(not(stage0))]
     fn try_recv(&self) -> Option<T> {
         unsafe {
             let (next_link_port, next_link_chan) = oneshot();
diff --git a/src/libstd/rt/context.rs b/src/libstd/rt/context.rs
index d5ca8473cee..09ba869549f 100644
--- a/src/libstd/rt/context.rs
+++ b/src/libstd/rt/context.rs
@@ -207,7 +207,7 @@ fn align_down(sp: *mut uint) -> *mut uint {
 }
 
 // XXX: ptr::offset is positive ints only
-#[inline(always)]
+#[inline]
 pub fn mut_offset<T>(ptr: *mut T, count: int) -> *mut T {
     use core::sys::size_of;
     (ptr as int + count * (size_of::<T>() as int)) as *mut T
diff --git a/src/libstd/rt/io/extensions.rs b/src/libstd/rt/io/extensions.rs
index c7c3eadbe21..55861f127bb 100644
--- a/src/libstd/rt/io/extensions.rs
+++ b/src/libstd/rt/io/extensions.rs
@@ -297,7 +297,8 @@ impl<T: Reader> ReaderUtil for T {
 
             do (|| {
                 while total_read < len {
-                    let slice = vec::mut_slice(*buf, start_len + total_read, buf.len());
+                    let len = buf.len();
+                    let slice = vec::mut_slice(*buf, start_len + total_read, len);
                     match self.read(slice) {
                         Some(nread) => {
                             total_read += nread;
diff --git a/src/libstd/rt/local_heap.rs b/src/libstd/rt/local_heap.rs
index 6bf228a1b22..f62c9fb2c66 100644
--- a/src/libstd/rt/local_heap.rs
+++ b/src/libstd/rt/local_heap.rs
@@ -10,11 +10,24 @@
 
 //! The local, garbage collected heap
 
+use libc;
 use libc::{c_void, uintptr_t, size_t};
 use ops::Drop;
+use repr::BoxRepr;
+use rt;
+use rt::OldTaskContext;
+use rt::local::Local;
+use rt::task::Task;
 
 type MemoryRegion = c_void;
-type BoxedRegion = c_void;
+
+struct Env { priv opaque: () }
+
+struct BoxedRegion {
+    env: *Env,
+    backing_region: *MemoryRegion,
+    live_allocs: *BoxRepr
+}
 
 pub type OpaqueBox = c_void;
 pub type TypeDesc = c_void;
@@ -49,6 +62,12 @@ impl LocalHeap {
         }
     }
 
+    pub fn realloc(&mut self, ptr: *OpaqueBox, size: uint) -> *OpaqueBox {
+        unsafe {
+            return rust_boxed_region_realloc(self.boxed_region, ptr, size as size_t);
+        }
+    }
+
     pub fn free(&mut self, box: *OpaqueBox) {
         unsafe {
             return rust_boxed_region_free(self.boxed_region, box);
@@ -65,6 +84,40 @@ impl Drop for LocalHeap {
     }
 }
 
+// A little compatibility function
+pub unsafe fn local_free(ptr: *libc::c_char) {
+    match rt::context() {
+        OldTaskContext => {
+            rust_upcall_free_noswitch(ptr);
+
+            extern {
+                #[fast_ffi]
+                unsafe fn rust_upcall_free_noswitch(ptr: *libc::c_char);
+            }
+        }
+        _ => {
+            do Local::borrow::<Task,()> |task| {
+                task.heap.free(ptr as *libc::c_void);
+            }
+        }
+    }
+}
+
+pub fn live_allocs() -> *BoxRepr {
+    let region = match rt::context() {
+        OldTaskContext => {
+            unsafe { rust_current_boxed_region() }
+        }
+        _ => {
+            do Local::borrow::<Task, *BoxedRegion> |task| {
+                task.heap.boxed_region
+            }
+        }
+    };
+
+    return unsafe { (*region).live_allocs };
+}
+
 extern {
     fn rust_new_memory_region(synchronized: uintptr_t,
                                detailed_leaks: uintptr_t,
@@ -76,5 +129,9 @@ extern {
     fn rust_boxed_region_malloc(region: *BoxedRegion,
                                 td: *TypeDesc,
                                 size: size_t) -> *OpaqueBox;
+    fn rust_boxed_region_realloc(region: *BoxedRegion,
+                                 ptr: *OpaqueBox,
+                                 size: size_t) -> *OpaqueBox;
     fn rust_boxed_region_free(region: *BoxedRegion, box: *OpaqueBox);
+    fn rust_current_boxed_region() -> *BoxedRegion;
 }
diff --git a/src/libstd/rt/logging.rs b/src/libstd/rt/logging.rs
index a0d05397689..84186180aa6 100644
--- a/src/libstd/rt/logging.rs
+++ b/src/libstd/rt/logging.rs
@@ -9,6 +9,7 @@
 // except according to those terms.
 
 use either::*;
+use libc;
 
 pub trait Logger {
     fn log(&mut self, msg: Either<~str, &'static str>);
@@ -20,6 +21,10 @@ impl Logger for StdErrLogger {
     fn log(&mut self, msg: Either<~str, &'static str>) {
         use io::{Writer, WriterUtil};
 
+        if !should_log_console() {
+            return;
+        }
+
         let s: &str = match msg {
             Left(ref s) => {
                 let s: &str = *s;
@@ -44,7 +49,6 @@ pub fn init(crate_map: *u8) {
     use str;
     use ptr;
     use option::{Some, None};
-    use libc::c_char;
 
     let log_spec = os::getenv("RUST_LOG");
     match log_spec {
@@ -61,8 +65,16 @@ pub fn init(crate_map: *u8) {
             }
         }
     }
+}
 
-    extern {
-        fn rust_update_log_settings(crate_map: *u8, settings: *c_char);
-    }
+pub fn console_on() { unsafe { rust_log_console_on() } }
+pub fn console_off() { unsafe { rust_log_console_off() } }
+fn should_log_console() -> bool { unsafe { rust_should_log_console() != 0 } }
+
+extern {
+    fn rust_update_log_settings(crate_map: *u8, settings: *libc::c_char);
+    fn rust_log_console_on();
+    fn rust_log_console_off();
+    fn rust_should_log_console() -> libc::uintptr_t;
 }
+
diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs
index 581e3addff0..bbf1cf0d9b7 100644
--- a/src/libstd/rt/mod.rs
+++ b/src/libstd/rt/mod.rs
@@ -58,22 +58,23 @@ Several modules in `core` are clients of `rt`:
 #[deny(unused_imports)];
 #[deny(unused_mut)];
 #[deny(unused_variable)];
+#[deny(unused_unsafe)];
 
 use cell::Cell;
 use clone::Clone;
 use container::Container;
-use from_str::FromStr;
+use iter::Times;
 use iterator::IteratorUtil;
-use option::{Some, None};
-use os;
+use option::Some;
 use ptr::RawPtr;
-use uint;
 use rt::sched::{Scheduler, Coroutine, Shutdown};
 use rt::sleeper_list::SleeperList;
 use rt::task::Task;
 use rt::thread::Thread;
 use rt::work_queue::WorkQueue;
 use rt::uv::uvio::UvEventLoop;
+use unstable::atomics::{AtomicInt, SeqCst};
+use unstable::sync::UnsafeAtomicRcBox;
 use vec::{OwnedVector, MutableVector};
 
 /// The global (exchange) heap.
@@ -122,7 +123,7 @@ mod thread;
 pub mod env;
 
 /// The local, managed heap
-mod local_heap;
+pub mod local_heap;
 
 /// The Logger trait and implementations
 pub mod logging;
@@ -148,7 +149,7 @@ pub mod local_ptr;
 /// Bindings to pthread/windows thread-local storage.
 pub mod thread_local_storage;
 
-/// A concurrent data structure with which parent tasks wait on child tasks.
+/// For waiting on child tasks.
 pub mod join_latch;
 
 pub mod metrics;
@@ -157,6 +158,12 @@ pub mod metrics;
 /// Just stuff
 pub mod util;
 
+// Global command line argument storage
+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
@@ -171,71 +178,104 @@ pub mod util;
 /// # 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, crate_map: *u8, main: ~fn()) -> int {
+pub fn start(argc: int, argv: **u8, crate_map: *u8, main: ~fn()) -> int {
 
-    init(crate_map);
-    run(main);
+    init(argc, argv, crate_map);
+    let exit_code = run(main);
     cleanup();
 
-    return 0;
+    return exit_code;
 }
 
-/// One-time runtime initialization. Currently all this does is set up logging
-/// based on the RUST_LOG environment variable.
-pub fn init(crate_map: *u8) {
-    logging::init(crate_map);
+/// One-time runtime initialization.
+///
+/// Initializes global state, including frobbing
+/// the crate's logging flags, registering GC
+/// metadata, and storing the process arguments.
+pub fn init(argc: int, argv: **u8, crate_map: *u8) {
+    // XXX: Derefing these pointers is not safe.
+    // Need to propagate the unsafety to `start`.
+    unsafe {
+        args::init(argc, argv);
+        logging::init(crate_map);
+        rust_update_gc_metadata(crate_map);
+    }
+
+    extern {
+        fn rust_update_gc_metadata(crate_map: *u8);
+    }
 }
 
+/// One-time runtime cleanup.
 pub fn cleanup() {
+    args::cleanup();
     global_heap::cleanup();
 }
 
-pub fn run(main: ~fn()) {
-    let nthreads = match os::getenv("RUST_THREADS") {
-        Some(nstr) => FromStr::from_str(nstr).get(),
-        None => unsafe {
-            // Using more threads than cores in test code
-            // to force the OS to preempt them frequently.
-            // Assuming that this help stress test concurrent types.
-            util::num_cpus() * 2
-        }
-    };
+/// 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: ~fn()) -> int {
+
+    static DEFAULT_ERROR_CODE: int = 101;
 
+    let nthreads = util::default_sched_threads();
+
+    // The shared list of sleeping schedulers. Schedulers wake each other
+    // occassionally to do new work.
     let sleepers = SleeperList::new();
+    // The shared work queue. Temporary until work stealing is implemented.
     let work_queue = WorkQueue::new();
 
-    let mut handles = ~[];
+    // 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 uint::range(0, nthreads) |_| {
+    for nthreads.times {
+        // Every scheduler is driven by an I/O event loop.
         let loop_ = ~UvEventLoop::new();
         let mut sched = ~Scheduler::new(loop_, work_queue.clone(), sleepers.clone());
         let handle = sched.make_handle();
 
-        handles.push(handle);
         scheds.push(sched);
+        handles.push(handle);
     }
 
-    let main_cell = Cell::new(main);
+    // Create a shared cell for transmitting the process exit
+    // code from the main task to this function.
+    let exit_code = UnsafeAtomicRcBox::new(AtomicInt::new(0));
+    let exit_code_clone = exit_code.clone();
+
+    // 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 = Cell::new(handles);
-    let mut new_task = ~Task::new_root();
-    let on_exit: ~fn(bool) = |exit_status| {
+    let on_exit: ~fn(bool) = |exit_success| {
 
         let mut handles = handles.take();
-        // Tell schedulers to exit
         for handles.mut_iter().advance |handle| {
             handle.send(Shutdown);
         }
 
-        rtassert!(exit_status);
+        unsafe {
+            let exit_code = if exit_success { 0 } else { DEFAULT_ERROR_CODE };
+            (*exit_code_clone.get()).store(exit_code, SeqCst);
+        }
     };
+
+    // Create and enqueue the main task.
+    let main_cell = Cell::new(main);
+    let mut new_task = ~Task::new_root();
     new_task.on_exit = Some(on_exit);
     let main_task = ~Coroutine::with_task(&mut scheds[0].stack_pool,
                                           new_task, main_cell.take());
     scheds[0].enqueue_task(main_task);
 
+    // Run each scheduler in a thread.
     let mut threads = ~[];
-
     while !scheds.is_empty() {
         let sched = scheds.pop();
         let sched_cell = Cell::new(sched);
@@ -248,7 +288,12 @@ pub fn run(main: ~fn()) {
     }
 
     // Wait for schedulers
-    let _threads = threads;
+    { let _threads = threads; }
+
+    // Return the exit code
+    unsafe {
+        (*exit_code.get()).load(SeqCst)
+    }
 }
 
 /// Possible contexts in which Rust code may be executing.
diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs
index 453d3303db6..bbe4aa25e29 100644
--- a/src/libstd/rt/sched.rs
+++ b/src/libstd/rt/sched.rs
@@ -646,12 +646,12 @@ impl Scheduler {
     }
 }
 
-// The cases for the below function.                                              
+// The cases for the below function.
 enum ResumeAction {
     SendHome,
     Requeue,
     ResumeNow,
-    Homeless                                                                      
+    Homeless
 }
 
 impl SchedHandle {
diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs
index e7f87906fe5..97c3b6a749b 100644
--- a/src/libstd/rt/task.rs
+++ b/src/libstd/rt/task.rs
@@ -15,6 +15,7 @@
 
 use borrow;
 use cast::transmute;
+use cleanup;
 use libc::{c_void, uintptr_t};
 use ptr;
 use prelude::*;
@@ -118,6 +119,10 @@ impl Task {
             }
             _ => ()
         }
+
+        // Destroy remaining boxes
+        unsafe { cleanup::annihilate(); }
+
         self.destroyed = true;
     }
 }
@@ -249,6 +254,18 @@ mod test {
     }
 
     #[test]
+    fn comm_shared_chan() {
+        use comm::*;
+
+        do run_in_newsched_task() {
+            let (port, chan) = stream();
+            let chan = SharedChan::new(chan);
+            chan.send(10);
+            assert!(port.recv() == 10);
+        }
+    }
+
+    #[test]
     fn linked_failure() {
         do run_in_newsched_task() {
             let res = do spawntask_try {
@@ -257,4 +274,45 @@ mod test {
             assert!(res.is_err());
         }
     }
+
+    #[test]
+    fn heap_cycles() {
+        use option::{Option, Some, None};
+
+        do run_in_newsched_task {
+            struct List {
+                next: Option<@mut List>,
+            }
+
+            let a = @mut List { next: None };
+            let b = @mut List { next: Some(a) };
+
+            a.next = Some(b);
+        }
+    }
+
+    // XXX: This is a copy of test_future_result in std::task.
+    // It can be removed once the scheduler is turned on by default.
+    #[test]
+    fn future_result() {
+        do run_in_newsched_task {
+            use option::{Some, None};
+            use task::*;
+
+            let mut result = None;
+            let mut builder = task();
+            builder.future_result(|r| result = Some(r));
+            do builder.spawn {}
+            assert_eq!(result.unwrap().recv(), Success);
+
+            result = None;
+            let mut builder = task();
+            builder.future_result(|r| result = Some(r));
+            builder.unlinked();
+            do builder.spawn {
+                fail!();
+            }
+            assert_eq!(result.unwrap().recv(), Failure);
+        }
+    }
 }
diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs
index 36efcd91834..b0e49684014 100644
--- a/src/libstd/rt/test.rs
+++ b/src/libstd/rt/test.rs
@@ -74,7 +74,7 @@ pub fn run_in_mt_newsched_task(f: ~fn()) {
     do run_in_bare_thread {
         let nthreads = match os::getenv("RUST_TEST_THREADS") {
             Some(nstr) => FromStr::from_str(nstr).get(),
-            None => unsafe {
+            None => {
                 // Using more threads than cores in test code
                 // to force the OS to preempt them frequently.
                 // Assuming that this help stress test concurrent types.
diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs
index 904b2f8bbb9..5219ae1d540 100644
--- a/src/libstd/rt/util.rs
+++ b/src/libstd/rt/util.rs
@@ -9,8 +9,11 @@
 // except according to those terms.
 
 use container::Container;
+use from_str::FromStr;
 use iterator::IteratorUtil;
 use libc;
+use option::{Some, None};
+use os;
 use str::StrSlice;
 
 /// Get the number of cores available
@@ -24,6 +27,15 @@ pub fn num_cpus() -> uint {
     }
 }
 
+/// Get's the number of scheduler threads requested by the environment
+/// either `RUST_THREADS` or `num_cpus`.
+pub fn default_sched_threads() -> uint {
+    match os::getenv("RUST_THREADS") {
+        Some(nstr) => FromStr::from_str(nstr).get(),
+        None => num_cpus()
+    }
+}
+
 pub fn dumb_println(s: &str) {
     use io::WriterUtil;
     let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
diff --git a/src/libstd/rt/uv/timer.rs b/src/libstd/rt/uv/timer.rs
index 5557a580987..14465eb7dfd 100644
--- a/src/libstd/rt/uv/timer.rs
+++ b/src/libstd/rt/uv/timer.rs
@@ -143,7 +143,7 @@ mod test {
             let count_ptr: *mut int = &mut count;
             let mut loop_ = Loop::new();
             let mut timer = TimerWatcher::new(&mut loop_);
-            do timer.start(10, 20) |timer, status| {
+            do timer.start(1, 2) |timer, status| {
                 assert!(status.is_none());
                 unsafe {
                     *count_ptr += 1;
@@ -160,14 +160,14 @@ mod test {
                         let mut timer2 = TimerWatcher::new(&mut loop_);
                         do timer2.start(10, 0) |timer2, _| {
 
-                            unsafe { *count_ptr += 1; }
+                            *count_ptr += 1;
 
                             timer2.close(||());
 
                             // Restart the original timer
                             let mut timer = timer;
-                            do timer.start(10, 0) |timer, _| {
-                                unsafe { *count_ptr += 1; }
+                            do timer.start(1, 0) |timer, _| {
+                                *count_ptr += 1;
                                 timer.close(||());
                             }
                         }