From e47ee779cd412ad629ab0f3db1babcc502862924 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Fri, 19 Apr 2013 15:18:38 -0700
Subject: [PATCH 01/28] wip

---
 src/libcore/rt/io/{util.rs => extensions.rs} |  0
 src/libcore/rt/io/file.rs                    |  6 +-----
 src/libcore/rt/io/mod.rs                     | 11 +++++------
 src/libcore/rt/io/net/unix.rs                |  2 +-
 src/libcore/rt/io/{misc.rs => support.rs}    |  0
 5 files changed, 7 insertions(+), 12 deletions(-)
 rename src/libcore/rt/io/{util.rs => extensions.rs} (100%)
 rename src/libcore/rt/io/{misc.rs => support.rs} (100%)

diff --git a/src/libcore/rt/io/util.rs b/src/libcore/rt/io/extensions.rs
similarity index 100%
rename from src/libcore/rt/io/util.rs
rename to src/libcore/rt/io/extensions.rs
diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs
index e041183b584..4e3e01a5ece 100644
--- a/src/libcore/rt/io/file.rs
+++ b/src/libcore/rt/io/file.rs
@@ -9,14 +9,10 @@
 // except according to those terms.
 
 use prelude::*;
-use super::misc::PathLike;
+use super::support::PathLike;
 use super::{Reader, Writer, Seek, Close};
 use super::{IoError, SeekStyle};
 
-/// Open a file with the default FileMode and FileAccess
-/// # XXX are there sane defaults here?
-pub fn open_file<P: PathLike>(_path: &P) -> FileStream { fail!() }
-
 /// # XXX
 /// * Ugh, this is ridiculous. What is the best way to represent these options?
 enum FileMode {
diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index b035532144c..7ab27cdd7ec 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -104,7 +104,6 @@ pub use self::stdio::stderr;
 pub use self::stdio::print;
 pub use self::stdio::println;
 
-pub use self::file::open_file;
 pub use self::file::FileStream;
 pub use self::net::Listener;
 pub use self::net::ip::IpAddr;
@@ -113,9 +112,9 @@ pub use self::net::tcp::TcpStream;
 pub use self::net::udp::UdpStream;
 
 // Some extension traits that all Readers and Writers get.
-pub use self::util::ReaderUtil;
-pub use self::util::ReaderByteConversions;
-pub use self::util::WriterByteConversions;
+pub use self::extensions::ReaderUtil;
+pub use self::extensions::ReaderByteConversions;
+pub use self::extensions::WriterByteConversions;
 
 /// Synchronous, non-blocking file I/O.
 pub mod file;
@@ -140,10 +139,10 @@ pub mod flate;
 pub mod comm_adapters;
 
 /// Extension traits
-mod util;
+mod extensions;
 
 /// Non-I/O things needed by the I/O module
-mod misc;
+mod support;
 
 /// Thread-blocking implementations
 pub mod native {
diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs
index 35eabe21b2a..de886949e7b 100644
--- a/src/libcore/rt/io/net/unix.rs
+++ b/src/libcore/rt/io/net/unix.rs
@@ -11,7 +11,7 @@
 use prelude::*;
 use super::*;
 use super::super::*;
-use super::super::misc::PathLike;
+use super::super::support::PathLike;
 
 pub struct UnixStream;
 
diff --git a/src/libcore/rt/io/misc.rs b/src/libcore/rt/io/support.rs
similarity index 100%
rename from src/libcore/rt/io/misc.rs
rename to src/libcore/rt/io/support.rs

From 6a5c4f68c2f8c10bf439a39373f5c518ed2f58a1 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Fri, 19 Apr 2013 18:47:31 -0700
Subject: [PATCH 02/28] core::rt: Just some poking at the I/O docs

---
 src/libcore/rt/io/mod.rs | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index 7ab27cdd7ec..c04389e0290 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -10,8 +10,12 @@
 
 /*! Synchronous I/O
 
-This module defines the Rust interface for synchronous I/O.
-It supports file access,
+This module defines the Rust interface for synchronous I/O.  It is
+build around Reader and Writer traits that define byte stream sources
+and sinks.  Implementations are provided for common I/O streams like
+file, TCP, UDP, Unix domain sockets, multiple types of memory bufers.
+Readers and Writers may be composed to add things like string parsing,
+and compression.
 
 This will likely live in core::io, not core::rt::io.
 
@@ -27,7 +31,7 @@ Some examples of obvious things you might want to do
 
 * Read a complete file to a string, (converting newlines?)
 
-    let contents = open("message.txt").read_to_str(); // read_to_str??
+    let contents = FileStream::open("message.txt").read_to_str(); // read_to_str??
 
 * Write a line to a file
 
@@ -36,13 +40,26 @@ Some examples of obvious things you might want to do
 
 * Iterate over the lines of a file
 
+    do FileStream::open("message.txt").each_line |line| {
+        println(line)
+    }
+
 * Pull the lines of a file into a vector of strings
 
+    let lines = FileStream::open("message.txt").line_iter().to_vec();
+
+* Make an simple HTTP request
+
+    let socket = TcpStream::open("localhost:8080");
+    socket.write_line("GET / HTTP/1.0");
+    socket.write_line("");
+    let response = socket.read_to_end();
+
 * Connect based on URL? Requires thinking about where the URL type lives
   and how to make protocol handlers extensible, e.g. the "tcp" protocol
   yields a `TcpStream`.
 
-    connect("tcp://localhost:8080").write_line("HTTP 1.0 GET /");
+    connect("tcp://localhost:8080");
 
 # Terms
 

From 1f97e6d47f90f1ddfef2d3a888099bd95b0bb1df Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 00:24:44 -0700
Subject: [PATCH 03/28] rt: Add rust_dbg_next_port for generating test port
 numbers

---
 src/rt/rust_test_helpers.cpp | 11 +++++++++++
 src/rt/rustrt.def.in         |  2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp
index 64966bd3454..7938d65acd2 100644
--- a/src/rt/rust_test_helpers.cpp
+++ b/src/rt/rust_test_helpers.cpp
@@ -165,3 +165,14 @@ extern "C" CDECL TwoDoubles
 rust_dbg_extern_identity_TwoDoubles(TwoDoubles u) {
     return u;
 }
+
+// Generates increasing port numbers for network testing
+extern "C" CDECL uintptr_t
+rust_dbg_next_port() {
+  static lock_and_signal dbg_port_lock;
+  static uintptr_t next_port = 9000;
+  scoped_lock with(dbg_port_lock);
+  uintptr_t this_port = next_port;
+  next_port += 1;
+  return this_port;
+}
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 5a556ed2107..f84a73a394b 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -221,4 +221,4 @@ rust_uv_free_ip4_addr
 rust_uv_free_ip6_addr
 rust_call_nullary_fn
 rust_initialize_global_state
-
+rust_dbg_next_port

From 744ba627f34ebf76b5a784085645780066d8d92a Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 00:33:49 -0700
Subject: [PATCH 04/28] core::rt: Add a test mod and put run_in_newsched_task
 there

---
 src/libcore/rt/mod.rs   |  4 ++++
 src/libcore/rt/test.rs  | 29 +++++++++++++++++++++++++++++
 src/libcore/task/mod.rs |  2 +-
 3 files changed, 34 insertions(+), 1 deletion(-)
 create mode 100644 src/libcore/rt/test.rs

diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs
index e93e0c6fc6c..70bc2c7e063 100644
--- a/src/libcore/rt/mod.rs
+++ b/src/libcore/rt/mod.rs
@@ -49,6 +49,10 @@ mod context;
 mod thread;
 pub mod env;
 
+/// Tools for testing the runtime
+#[cfg(test)]
+pub mod test;
+
 #[cfg(stage0)]
 pub fn start(main: *u8, _argc: int, _argv: *c_char, _crate_map: *u8) -> int {
     use self::sched::{Scheduler, Task};
diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
new file mode 100644
index 00000000000..14d69c23732
--- /dev/null
+++ b/src/libcore/rt/test.rs
@@ -0,0 +1,29 @@
+// Copyright 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.
+
+// For setting up tests of the new scheduler
+pub fn run_in_newsched_task(f: ~fn()) {
+    use cell::Cell;
+    use unstable::run_in_bare_thread;
+    use super::sched::Task;
+    use super::uvio::UvEventLoop;
+
+    let f = Cell(Cell(f));
+
+    do run_in_bare_thread {
+        let mut sched = ~UvEventLoop::new_scheduler();
+        let f = f.take();
+        let task = ~do Task::new(&mut sched.stack_pool) {
+            (f.take())();
+        };
+        sched.task_queue.push_back(task);
+        sched.run();
+    }
+}
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index a6c03638713..a243bfba85c 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -1229,7 +1229,7 @@ fn test_spawn_thread_on_demand() {
 
 #[test]
 fn test_simple_newsched_spawn() {
-    use rt::run_in_newsched_task;
+    use rt::test::run_in_newsched_task;
 
     do run_in_newsched_task {
         spawn(||())

From d24a3a4b016331438ec3c611e37c11cfe41fa4af Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 01:16:06 -0700
Subject: [PATCH 05/28] core::rt: Use generated port numbers in tests

---
 src/libcore/rt/test.rs       | 10 ++++++++
 src/libcore/rt/uv/net.rs     | 48 +++++-------------------------------
 src/libcore/rt/uvio.rs       | 13 +++++-----
 src/rt/rust_test_helpers.cpp |  2 +-
 4 files changed, 24 insertions(+), 49 deletions(-)

diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index 14d69c23732..c2ad7d37d42 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -27,3 +27,13 @@ pub fn run_in_newsched_task(f: ~fn()) {
         sched.run();
     }
 }
+
+/// Get a port number, starting at 9600, for use in tests
+pub fn next_test_port() -> u16 {
+    unsafe {
+        return rust_dbg_next_port() as u16;
+    }
+    extern {
+        fn rust_dbg_next_port() -> ::libc::uintptr_t;
+    }
+}
diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs
index 0dc1a4d86cb..54823d4dbcf 100644
--- a/src/libcore/rt/uv/net.rs
+++ b/src/libcore/rt/uv/net.rs
@@ -19,12 +19,10 @@ use super::{Loop, Watcher, Request, UvError, Buf, Callback, NativeHandle, NullCa
             vec_to_uv_buf, vec_from_uv_buf};
 use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6};
 
-#[cfg(test)]
-use unstable::run_in_bare_thread;
-#[cfg(test)]
-use super::super::thread::Thread;
-#[cfg(test)]
-use cell::Cell;
+#[cfg(test)] use cell::Cell;
+#[cfg(test)] use unstable::run_in_bare_thread;
+#[cfg(test)] use super::super::thread::Thread;
+#[cfg(test)] use super::super::test::next_test_port;
 
 fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) {
     match addr {
@@ -361,7 +359,7 @@ fn connect_close() {
         let mut loop_ = Loop::new();
         let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
         // Connect to a port where nobody is listening
-        let addr = Ipv4(127, 0, 0, 1, 2923);
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
         do tcp_watcher.connect(addr) |stream_watcher, status| {
             rtdebug!("tcp_watcher.connect!");
             assert!(status.is_some());
@@ -373,47 +371,13 @@ fn connect_close() {
     }
 }
 
-#[test]
-#[ignore(reason = "need a server to connect to")]
-fn connect_read() {
-    do run_in_bare_thread() {
-        let mut loop_ = Loop::new();
-        let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
-        let addr = Ipv4(127, 0, 0, 1, 2924);
-        do tcp_watcher.connect(addr) |stream_watcher, status| {
-            let mut stream_watcher = stream_watcher;
-            rtdebug!("tcp_watcher.connect!");
-            assert!(status.is_none());
-            let alloc: AllocCallback = |size| {
-                vec_to_uv_buf(vec::from_elem(size, 0))
-            };
-            do stream_watcher.read_start(alloc)
-                |stream_watcher, nread, buf, status| {
-
-                let buf = vec_from_uv_buf(buf);
-                rtdebug!("read cb!");
-                if status.is_none() {
-                    let bytes = buf.unwrap();
-                    rtdebug!("%s", bytes.slice(0, nread as uint).to_str());
-                } else {
-                    rtdebug!("status after read: %s", status.get().to_str());
-                    rtdebug!("closing");
-                    stream_watcher.close(||());
-                }
-            }
-        }
-        loop_.run();
-        loop_.close();
-    }
-}
-
 #[test]
 fn listen() {
     do run_in_bare_thread() {
         static MAX: int = 10;
         let mut loop_ = Loop::new();
         let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) };
-        let addr = Ipv4(127, 0, 0, 1, 2925);
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
         server_tcp_watcher.bind(addr);
         let loop_ = loop_;
         rtdebug!("listening");
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index ff539739835..a9aa0333b16 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -19,9 +19,10 @@ use cell::{Cell, empty_cell};
 use cast::transmute;
 use super::sched::{Scheduler, local_sched};
 
-#[cfg(test)] use super::sched::Task;
-#[cfg(test)] use unstable::run_in_bare_thread;
 #[cfg(test)] use uint;
+#[cfg(test)] use unstable::run_in_bare_thread;
+#[cfg(test)] use super::sched::Task;
+#[cfg(test)] use super::test::next_test_port;
 
 pub struct UvEventLoop {
     uvio: UvIoFactory
@@ -340,7 +341,7 @@ fn test_simple_io_no_connect() {
         let mut sched = ~UvEventLoop::new_scheduler();
         let task = ~do Task::new(&mut sched.stack_pool) {
             let io = unsafe { local_sched::unsafe_borrow_io() };
-            let addr = Ipv4(127, 0, 0, 1, 2926);
+            let addr = Ipv4(127, 0, 0, 1, next_test_port());
             let maybe_chan = io.connect(addr);
             assert!(maybe_chan.is_none());
         };
@@ -354,7 +355,7 @@ fn test_simple_io_no_connect() {
 fn test_simple_tcp_server_and_client() {
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();
-        let addr = Ipv4(127, 0, 0, 1, 2929);
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
         let client_task = ~do Task::new(&mut sched.stack_pool) {
             unsafe {
@@ -393,7 +394,7 @@ fn test_simple_tcp_server_and_client() {
 fn test_read_and_block() {
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();
-        let addr = Ipv4(127, 0, 0, 1, 2930);
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
         let client_task = ~do Task::new(&mut sched.stack_pool) {
             let io = unsafe { local_sched::unsafe_borrow_io() };
@@ -454,7 +455,7 @@ fn test_read_and_block() {
 fn test_read_read_read() {
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();
-        let addr = Ipv4(127, 0, 0, 1, 2931);
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
         let client_task = ~do Task::new(&mut sched.stack_pool) {
             let io = unsafe { local_sched::unsafe_borrow_io() };
diff --git a/src/rt/rust_test_helpers.cpp b/src/rt/rust_test_helpers.cpp
index 7938d65acd2..d82c39d6838 100644
--- a/src/rt/rust_test_helpers.cpp
+++ b/src/rt/rust_test_helpers.cpp
@@ -170,7 +170,7 @@ rust_dbg_extern_identity_TwoDoubles(TwoDoubles u) {
 extern "C" CDECL uintptr_t
 rust_dbg_next_port() {
   static lock_and_signal dbg_port_lock;
-  static uintptr_t next_port = 9000;
+  static uintptr_t next_port = 9600;
   scoped_lock with(dbg_port_lock);
   uintptr_t this_port = next_port;
   next_port += 1;

From eac629bf5c4b9efd0de3ff6a14d63584eb4a967d Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 01:32:06 -0700
Subject: [PATCH 06/28] core::rt: Unignore some networking tests

These should work now, I hope
---
 src/libcore/rt/uvio.rs | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index a9aa0333b16..c615f068a69 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -335,7 +335,6 @@ impl Stream for UvStream {
 }
 
 #[test]
-#[ignore(reason = "ffi struct issues")]
 fn test_simple_io_no_connect() {
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();
@@ -351,7 +350,6 @@ fn test_simple_io_no_connect() {
 }
 
 #[test]
-#[ignore(reason = "ffi struct issues")]
 fn test_simple_tcp_server_and_client() {
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();

From 1a7561be4d2046a6a6b78ae5dab2e6fd3ba8db82 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 01:37:59 -0700
Subject: [PATCH 07/28] core::rt: Remove redundant copy of run_in_newsched_task

---
 src/libcore/rt/mod.rs | 21 ---------------------
 1 file changed, 21 deletions(-)

diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs
index 70bc2c7e063..cf896dd18f3 100644
--- a/src/libcore/rt/mod.rs
+++ b/src/libcore/rt/mod.rs
@@ -164,24 +164,3 @@ fn test_context() {
         sched.run();
     }
 }
-
-// For setting up tests of the new scheduler
-#[cfg(test)]
-pub fn run_in_newsched_task(f: ~fn()) {
-    use cell::Cell;
-    use unstable::run_in_bare_thread;
-    use self::sched::Task;
-    use self::uvio::UvEventLoop;
-
-    let f = Cell(Cell(f));
-
-    do run_in_bare_thread {
-        let mut sched = ~UvEventLoop::new_scheduler();
-        let f = f.take();
-        let task = ~do Task::new(&mut sched.stack_pool) {
-            (f.take())();
-        };
-        sched.task_queue.push_back(task);
-        sched.run();
-    }
-}

From e564fc7f6b02e917407b3aa6235fc0727afdaf2c Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 01:55:10 -0700
Subject: [PATCH 08/28] core::rt: Don't directly create scheduler types in I/O
 tests

There are some better abstractions for this now
---
 src/libcore/rt/test.rs | 16 ++++++++
 src/libcore/rt/uvio.rs | 87 +++++++++++++++++-------------------------
 2 files changed, 50 insertions(+), 53 deletions(-)

diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index c2ad7d37d42..dfc058d6eba 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -28,6 +28,22 @@ pub fn run_in_newsched_task(f: ~fn()) {
     }
 }
 
+/// Create a new task and run it right now
+pub fn spawn_immediately(f: ~fn()) {
+    use cell::Cell;
+    use super::*;
+    use super::sched::*;
+
+    let mut sched = local_sched::take();
+    let task = ~Task::new(&mut sched.stack_pool, f);
+    do sched.switch_running_tasks_and_then(task) |task| {
+        let task = Cell(task);
+        do local_sched::borrow |sched| {
+            sched.task_queue.push_front(task.take());
+        }
+    }
+}
+
 /// Get a port number, starting at 9600, for use in tests
 pub fn next_test_port() -> u16 {
     unsafe {
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index c615f068a69..d3953b7a797 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -22,7 +22,7 @@ use super::sched::{Scheduler, local_sched};
 #[cfg(test)] use uint;
 #[cfg(test)] use unstable::run_in_bare_thread;
 #[cfg(test)] use super::sched::Task;
-#[cfg(test)] use super::test::next_test_port;
+#[cfg(test)] use super::test::*;
 
 pub struct UvEventLoop {
     uvio: UvIoFactory
@@ -336,35 +336,21 @@ impl Stream for UvStream {
 
 #[test]
 fn test_simple_io_no_connect() {
-    do run_in_bare_thread {
-        let mut sched = ~UvEventLoop::new_scheduler();
-        let task = ~do Task::new(&mut sched.stack_pool) {
-            let io = unsafe { local_sched::unsafe_borrow_io() };
-            let addr = Ipv4(127, 0, 0, 1, next_test_port());
-            let maybe_chan = io.connect(addr);
-            assert!(maybe_chan.is_none());
-        };
-        sched.task_queue.push_back(task);
-        sched.run();
+    do run_in_newsched_task {
+        let io = unsafe { local_sched::unsafe_borrow_io() };
+        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let maybe_chan = io.connect(addr);
+        assert!(maybe_chan.is_none());
     }
 }
 
 #[test]
 fn test_simple_tcp_server_and_client() {
-    do run_in_bare_thread {
-        let mut sched = ~UvEventLoop::new_scheduler();
+    do run_in_newsched_task {
         let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
-        let client_task = ~do Task::new(&mut sched.stack_pool) {
-            unsafe {
-                let io = local_sched::unsafe_borrow_io();
-                let mut stream = io.connect(addr).unwrap();
-                stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
-                stream.close();
-            }
-        };
-
-        let server_task = ~do Task::new(&mut sched.stack_pool) {
+        // Start the server first so it's listening when we connect
+        do spawn_immediately {
             unsafe {
                 let io = local_sched::unsafe_borrow_io();
                 let mut listener = io.bind(addr).unwrap();
@@ -379,32 +365,25 @@ fn test_simple_tcp_server_and_client() {
                 stream.close();
                 listener.close();
             }
-        };
+        }
 
-        // Start the server first so it listens before the client connects
-        sched.task_queue.push_back(server_task);
-        sched.task_queue.push_back(client_task);
-        sched.run();
+        do spawn_immediately {
+            unsafe {
+                let io = local_sched::unsafe_borrow_io();
+                let mut stream = io.connect(addr).unwrap();
+                stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
+                stream.close();
+            }
+        }
     }
 }
 
 #[test] #[ignore(reason = "busted")]
 fn test_read_and_block() {
-    do run_in_bare_thread {
-        let mut sched = ~UvEventLoop::new_scheduler();
+    do run_in_newsched_task {
         let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
-        let client_task = ~do Task::new(&mut sched.stack_pool) {
-            let io = unsafe { local_sched::unsafe_borrow_io() };
-            let mut stream = io.connect(addr).unwrap();
-            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
-            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
-            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
-            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
-            stream.close();
-        };
-
-        let server_task = ~do Task::new(&mut sched.stack_pool) {
+        do spawn_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut listener = io.bind(addr).unwrap();
             let mut stream = listener.listen().unwrap();
@@ -440,22 +419,27 @@ fn test_read_and_block() {
 
             stream.close();
             listener.close();
-        };
+        }
+
+        do spawn_immediately {
+            let io = unsafe { local_sched::unsafe_borrow_io() };
+            let mut stream = io.connect(addr).unwrap();
+            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
+            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
+            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
+            stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
+            stream.close();
+        }
 
-        // Start the server first so it listens before the client connects
-        sched.task_queue.push_back(server_task);
-        sched.task_queue.push_back(client_task);
-        sched.run();
     }
 }
 
 #[test] #[ignore(reason = "needs server")]
 fn test_read_read_read() {
-    do run_in_bare_thread {
-        let mut sched = ~UvEventLoop::new_scheduler();
+    do run_in_newsched_task {
         let addr = Ipv4(127, 0, 0, 1, next_test_port());
 
-        let client_task = ~do Task::new(&mut sched.stack_pool) {
+        do spawn_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut stream = io.connect(addr).unwrap();
             let mut buf = [0, .. 2048];
@@ -467,9 +451,6 @@ fn test_read_read_read() {
             }
             rtdebug_!("read %u bytes total", total_bytes_read as uint);
             stream.close();
-        };
-
-        sched.task_queue.push_back(client_task);
-        sched.run();
+        }
     }
 }

From a11c032f36658667bb08382cc409455b0a1d0a61 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 02:16:21 -0700
Subject: [PATCH 09/28] core::rt: Fix a broken uvio test

---
 src/libcore/rt/uvio.rs | 23 ++++++++++++++++++++---
 1 file changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index d3953b7a797..2582ddf3105 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -434,22 +434,39 @@ fn test_read_and_block() {
     }
 }
 
-#[test] #[ignore(reason = "needs server")]
+#[test]
 fn test_read_read_read() {
     do run_in_newsched_task {
         let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        static MAX: uint = 5000000;
+        
+        do spawn_immediately {
+            unsafe {
+                let io = local_sched::unsafe_borrow_io();
+                let mut listener = io.bind(addr).unwrap();
+                let mut stream = listener.listen().unwrap();
+                let mut buf = [0, .. 2048];
+                let mut total_bytes_written = 0;
+                while total_bytes_written < MAX {
+                    stream.write(buf);
+                    total_bytes_written += buf.len();
+                }
+                stream.close();
+                listener.close();
+            }
+        }
 
         do spawn_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut stream = io.connect(addr).unwrap();
             let mut buf = [0, .. 2048];
             let mut total_bytes_read = 0;
-            while total_bytes_read < 500000000 {
+            while total_bytes_read < MAX {
                 let nread = stream.read(buf).unwrap();
                 rtdebug!("read %u bytes", nread as uint);
                 total_bytes_read += nread;
             }
-            rtdebug_!("read %u bytes total", total_bytes_read as uint);
+            rtdebug!("read %u bytes total", total_bytes_read as uint);
             stream.close();
         }
     }

From 6e17202ff4a694a2cf96fd7d2ccb1d62265ce7c2 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 02:41:30 -0700
Subject: [PATCH 10/28] core::rt: Add `next_test_ip4` for generating test
 addresses

---
 src/libcore/rt/test.rs   | 7 +++++++
 src/libcore/rt/uv/net.rs | 6 +++---
 src/libcore/rt/uvio.rs   | 8 ++++----
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index dfc058d6eba..585d43a3ade 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use super::io::net::ip::{IpAddr, Ipv4};
+
 // For setting up tests of the new scheduler
 pub fn run_in_newsched_task(f: ~fn()) {
     use cell::Cell;
@@ -53,3 +55,8 @@ pub fn next_test_port() -> u16 {
         fn rust_dbg_next_port() -> ::libc::uintptr_t;
     }
 }
+
+/// Get a unique localhost:port pair starting at 9600
+pub fn next_test_ip4() -> IpAddr {
+    Ipv4(127, 0, 0, 1, next_test_port())
+}
diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs
index 54823d4dbcf..860c988b9c9 100644
--- a/src/libcore/rt/uv/net.rs
+++ b/src/libcore/rt/uv/net.rs
@@ -22,7 +22,7 @@ use super::super::io::net::ip::{IpAddr, Ipv4, Ipv6};
 #[cfg(test)] use cell::Cell;
 #[cfg(test)] use unstable::run_in_bare_thread;
 #[cfg(test)] use super::super::thread::Thread;
-#[cfg(test)] use super::super::test::next_test_port;
+#[cfg(test)] use super::super::test::*;
 
 fn ip4_as_uv_ip4(addr: IpAddr, f: &fn(*sockaddr_in)) {
     match addr {
@@ -359,7 +359,7 @@ fn connect_close() {
         let mut loop_ = Loop::new();
         let mut tcp_watcher = { TcpWatcher::new(&mut loop_) };
         // Connect to a port where nobody is listening
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
         do tcp_watcher.connect(addr) |stream_watcher, status| {
             rtdebug!("tcp_watcher.connect!");
             assert!(status.is_some());
@@ -377,7 +377,7 @@ fn listen() {
         static MAX: int = 10;
         let mut loop_ = Loop::new();
         let mut server_tcp_watcher = { TcpWatcher::new(&mut loop_) };
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
         server_tcp_watcher.bind(addr);
         let loop_ = loop_;
         rtdebug!("listening");
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index 2582ddf3105..92439a41b34 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -338,7 +338,7 @@ impl Stream for UvStream {
 fn test_simple_io_no_connect() {
     do run_in_newsched_task {
         let io = unsafe { local_sched::unsafe_borrow_io() };
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
         let maybe_chan = io.connect(addr);
         assert!(maybe_chan.is_none());
     }
@@ -347,7 +347,7 @@ fn test_simple_io_no_connect() {
 #[test]
 fn test_simple_tcp_server_and_client() {
     do run_in_newsched_task {
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
 
         // Start the server first so it's listening when we connect
         do spawn_immediately {
@@ -381,7 +381,7 @@ fn test_simple_tcp_server_and_client() {
 #[test] #[ignore(reason = "busted")]
 fn test_read_and_block() {
     do run_in_newsched_task {
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
 
         do spawn_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
@@ -437,7 +437,7 @@ fn test_read_and_block() {
 #[test]
 fn test_read_read_read() {
     do run_in_newsched_task {
-        let addr = Ipv4(127, 0, 0, 1, next_test_port());
+        let addr = next_test_ip4();
         static MAX: uint = 5000000;
         
         do spawn_immediately {

From bcb3cfb8ce8353ef91b8b7acc0d6339c809d9a29 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 15:55:07 -0700
Subject: [PATCH 11/28] core::rt: Improve docs for run_in_newsched_task testing
 function

---
 src/libcore/rt/test.rs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index 585d43a3ade..e394a873fea 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -10,7 +10,8 @@
 
 use super::io::net::ip::{IpAddr, Ipv4};
 
-// For setting up tests of the new scheduler
+/// Creates a new scheduler in a new thread and runs a task in it,
+/// then waits for the scheduler to exit.
 pub fn run_in_newsched_task(f: ~fn()) {
     use cell::Cell;
     use unstable::run_in_bare_thread;

From d818ea81549a2ce12ba7b722997567f45417f099 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 15:55:37 -0700
Subject: [PATCH 12/28] core::rt: Listener constructors are called  and return
 a

---
 src/libcore/rt/io/net/tcp.rs  | 29 ++++++++++++++++++++++++++++-
 src/libcore/rt/io/net/udp.rs  |  2 +-
 src/libcore/rt/io/net/unix.rs |  2 +-
 3 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs
index e3f71dca8c8..d78241b8e44 100644
--- a/src/libcore/rt/io/net/tcp.rs
+++ b/src/libcore/rt/io/net/tcp.rs
@@ -40,7 +40,7 @@ impl Close for TcpStream {
 pub struct TcpListener;
 
 impl TcpListener {
-    pub fn new(_addr: IpAddr) -> TcpListener {
+    pub fn bind(_addr: IpAddr) -> Result<TcpListener, IoError> {
         fail!()
     }
 }
@@ -48,3 +48,30 @@ impl TcpListener {
 impl Listener<TcpStream> for TcpListener {
     fn accept(&mut self) -> Option<TcpStream> { fail!() }
 }
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use rt::test::*;
+
+    #[test] #[ignore]
+    fn smoke_test() {
+        /*do run_in_newsched_task {
+            let addr = next_test_ip4();
+
+            do spawn_immediately {
+                let listener = TcpListener::bind(addr);
+                do listener.accept() {
+                    let mut buf = [0];
+                    listener.read(buf);
+                    assert!(buf[0] == 99);
+                }
+            }
+
+            do spawn_immediately {
+                let stream = TcpStream::connect(addr);
+                stream.write([99]);
+            }
+        }*/
+    }
+}
diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs
index f76bb58a45e..81a6677c14a 100644
--- a/src/libcore/rt/io/net/udp.rs
+++ b/src/libcore/rt/io/net/udp.rs
@@ -40,7 +40,7 @@ impl Close for UdpStream {
 pub struct UdpListener;
 
 impl UdpListener {
-    pub fn new(_addr: IpAddr) -> UdpListener {
+    pub fn bind(_addr: IpAddr) -> Result<UdpListener, IoError> {
         fail!()
     }
 }
diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs
index de886949e7b..a5f4f8e3ba8 100644
--- a/src/libcore/rt/io/net/unix.rs
+++ b/src/libcore/rt/io/net/unix.rs
@@ -40,7 +40,7 @@ impl Close for UnixStream {
 pub struct UnixListener;
 
 impl UnixListener {
-    pub fn new<P: PathLike>(_path: &P) -> UnixListener {
+    pub fn bind<P: PathLike>(_path: &P) -> Result<UnixListener, IoError> {
         fail!()
     }
 }

From 00474c13f32db8903e0c9ea4ab6186007a79e77c Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 16:15:06 -0700
Subject: [PATCH 13/28] core: Ignore two long-running tests

---
 src/libcore/run.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/libcore/run.rs b/src/libcore/run.rs
index 8b18cc3c696..96f88e63680 100644
--- a/src/libcore/run.rs
+++ b/src/libcore/run.rs
@@ -598,12 +598,14 @@ mod tests {
 
     #[test]
     #[cfg(unix)]
+    #[ignore(reason = "long run time")]
     fn test_unforced_destroy_actually_kills() {
         test_destroy_actually_kills(false);
     }
 
     #[test]
     #[cfg(unix)]
+    #[ignore(reason = "long run time")]
     fn test_forced_destroy_actually_kills() {
         test_destroy_actually_kills(true);
     }

From fa478f577565174327888ea768dfa3a1018416f2 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 16:15:32 -0700
Subject: [PATCH 14/28] core: Speed up a test case

---
 src/libcore/rt/uvio.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index 92439a41b34..2e9d0afa52f 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -438,7 +438,7 @@ fn test_read_and_block() {
 fn test_read_read_read() {
     do run_in_newsched_task {
         let addr = next_test_ip4();
-        static MAX: uint = 5000000;
+        static MAX: uint = 500000;
         
         do spawn_immediately {
             unsafe {

From 4eff3130c589b5fb256c537e90272646fd8406ab Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sat, 20 Apr 2013 17:25:00 -0700
Subject: [PATCH 15/28] core::rt:: Implement Reader/Writer for
 MemReader/MemWriter

---
 src/libcore/rt/io/mem.rs | 69 ++++++++++++++++++++++++++++++++++++----
 src/libcore/rt/io/mod.rs |  8 +++++
 2 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/src/libcore/rt/io/mem.rs b/src/libcore/rt/io/mem.rs
index 600968a3c71..06e1466831d 100644
--- a/src/libcore/rt/io/mem.rs
+++ b/src/libcore/rt/io/mem.rs
@@ -17,7 +17,7 @@
 
 use prelude::*;
 use super::*;
-
+use cmp::min;
 
 /// Writes to an owned, growable byte vector
 pub struct MemWriter {
@@ -29,13 +29,15 @@ impl MemWriter {
 }
 
 impl Writer for MemWriter {
-    fn write(&mut self, _buf: &[u8]) { fail!() }
+    fn write(&mut self, buf: &[u8]) {
+        self.buf.push_all(buf)
+    }
 
     fn flush(&mut self) { /* no-op */ }
 }
 
 impl Seek for MemWriter {
-    fn tell(&self) -> u64 { fail!() }
+    fn tell(&self) -> u64 { self.buf.len() as u64 }
 
     fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
 }
@@ -77,13 +79,27 @@ impl MemReader {
 }
 
 impl Reader for MemReader {
-    fn read(&mut self, _buf: &mut [u8]) -> Option<uint> { fail!() }
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        { if self.eof() { return None; } }
 
-    fn eof(&mut self) -> bool { fail!() }
+        let write_len = min(buf.len(), self.buf.len() - self.pos);
+        {
+            let input = self.buf.slice(self.pos, self.pos + write_len);
+            let output = vec::mut_slice(buf, 0, write_len);
+            assert!(input.len() == output.len());
+            vec::bytes::copy_memory(output, input, write_len);
+        }
+        self.pos += write_len;
+        assert!(self.pos <= self.buf.len());
+
+        return Some(write_len);
+    }
+
+    fn eof(&mut self) -> bool { self.pos == self.buf.len() }
 }
 
 impl Seek for MemReader {
-    fn tell(&self) -> u64 { fail!() }
+    fn tell(&self) -> u64 { self.pos as u64 }
 
     fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
 }
@@ -163,4 +179,43 @@ impl<'self> Seek for BufReader<'self> {
     fn tell(&self) -> u64 { fail!() }
 
     fn seek(&mut self, _pos: i64, _style: SeekStyle) { fail!() }
-}
\ No newline at end of file
+}
+
+#[cfg(test)]
+mod test {
+    use prelude::*;
+    use super::*;
+
+    #[test]
+    fn test_mem_writer() {
+        let mut writer = MemWriter::new();
+        assert!(writer.tell() == 0);
+        writer.write([0]);
+        assert!(writer.tell() == 1);
+        writer.write([1, 2, 3]);
+        writer.write([4, 5, 6, 7]);
+        assert!(writer.tell() == 8);
+        assert!(writer.inner() == ~[0, 1, 2, 3, 4, 5 , 6, 7]);
+    }
+
+    #[test]
+    fn test_mem_reader() {
+        let mut reader = MemReader::new(~[0, 1, 2, 3, 4, 5, 6, 7]);
+        let mut buf = [];
+        assert!(reader.read(buf) == Some(0));
+        assert!(reader.tell() == 0);
+        let mut buf = [0];
+        assert!(reader.read(buf) == Some(1));
+        assert!(reader.tell() == 1);
+        assert!(buf == [0]);
+        let mut buf = [0, ..4];
+        assert!(reader.read(buf) == Some(4));
+        assert!(reader.tell() == 5);
+        assert!(buf == [1, 2, 3, 4]);
+        assert!(reader.read(buf) == Some(3));
+        assert!(buf.slice(0, 3) == [5, 6, 7]);
+        assert!(reader.eof());
+        assert!(reader.read(buf) == None);
+        assert!(reader.eof());
+    }
+}
diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index c04389e0290..238bd97a62d 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -269,6 +269,14 @@ pub enum SeekStyle {
 /// * Are `u64` and `i64` the right choices?
 pub trait Seek {
     fn tell(&self) -> u64;
+
+    /// Seek to an offset in a stream
+    ///
+    /// A successful seek clears the EOF indicator.
+    ///
+    /// # XXX
+    ///
+    /// * What is the behavior when seeking past the end of a stream?
     fn seek(&mut self, pos: i64, style: SeekStyle);
 }
 

From f9069baa70ea78117f2087fe6e359fb2ea0ae16a Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sun, 21 Apr 2013 16:28:17 -0700
Subject: [PATCH 16/28] core::rt: Add LocalServices for thread-local language
 services

Things like the GC heap and unwinding are desirable everywhere the language
might be used, not just in tasks. All Rust code should have access to
LocalServices.
---
 src/libcore/rt/local_services.rs | 63 ++++++++++++++++++++++++++++++++
 src/libcore/rt/mod.rs            |  3 +-
 src/libcore/rt/sched/mod.rs      |  6 ++-
 3 files changed, 70 insertions(+), 2 deletions(-)
 create mode 100644 src/libcore/rt/local_services.rs

diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
new file mode 100644
index 00000000000..8e386f80b77
--- /dev/null
+++ b/src/libcore/rt/local_services.rs
@@ -0,0 +1,63 @@
+// Copyright 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.
+
+//! Language-level runtime services that should reasonably expected
+//! to be available 'everywhere'. Local heaps, GC, unwinding,
+//! local storage, and logging. Even a 'freestanding' Rust would likely want
+//! to implement this.
+
+//! Local services may exist in at least three different contexts:
+//! when running as a task, when running in the scheduler's context,
+//! or when running outside of a scheduler but with local services
+//! (freestanding rust with local services?).
+
+use prelude::*;
+use super::sched::{Task, local_sched};
+
+pub struct LocalServices {
+    heap: LocalHeap,
+    gc: GarbageCollector,
+    storage: LocalStorage,
+    logger: Logger,
+    unwinder: Unwinder
+}
+
+pub struct LocalHeap;
+pub struct GarbageCollector;
+pub struct LocalStorage;
+pub struct Logger;
+pub struct Unwinder;
+
+impl LocalServices {
+    pub fn new() -> LocalServices {
+        LocalServices {
+            heap: LocalHeap,
+            gc: GarbageCollector,
+            storage: LocalStorage,
+            logger: Logger,
+            unwinder: Unwinder
+        }
+    }
+}
+
+/// Borrow a pointer to the installed local services.
+/// Fails (likely aborting the process) if local services are not available.
+pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
+    do local_sched::borrow |sched| {
+        match sched.current_task {
+            Some(~ref mut task) => {
+                f(&mut task.local_services)
+            }
+            None => {
+                fail!(~"no local services for schedulers yet")
+            }
+        }
+    }
+}
diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs
index cf896dd18f3..e0190418b13 100644
--- a/src/libcore/rt/mod.rs
+++ b/src/libcore/rt/mod.rs
@@ -48,6 +48,7 @@ mod stack;
 mod context;
 mod thread;
 pub mod env;
+pub mod local_services;
 
 /// Tools for testing the runtime
 #[cfg(test)]
@@ -97,7 +98,7 @@ pub fn start(main: *u8, _argc: int, _argv: **c_char, _crate_map: *u8) -> int {
 /// Different runtime services are available depending on context.
 #[deriving(Eq)]
 pub enum RuntimeContext {
-    // Only default services, e.g. exchange heap
+    // Only the exchange heap is available
     GlobalContext,
     // The scheduler may be accessed
     SchedulerContext,
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index 28946281628..30136e443ee 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -16,6 +16,7 @@ use super::work_queue::WorkQueue;
 use super::stack::{StackPool, StackSegment};
 use super::rtio::{EventLoop, EventLoopObject};
 use super::context::Context;
+use super::local_services::LocalServices;
 use cell::Cell;
 
 #[cfg(test)] use super::uvio::UvEventLoop;
@@ -38,7 +39,7 @@ pub struct Scheduler {
     /// Always valid when a task is executing, otherwise not
     priv saved_context: Context,
     /// The currently executing task
-    priv current_task: Option<~Task>,
+    current_task: Option<~Task>,
     /// An action performed after a context switch on behalf of the
     /// code running before the context switch
     priv cleanup_job: Option<CleanupJob>
@@ -326,6 +327,8 @@ pub struct Task {
     /// These are always valid when the task is not running, unless
     /// the task is dead
     priv saved_context: Context,
+    /// The heap, GC, unwinding, local storage, logging
+    local_services: LocalServices
 }
 
 pub impl Task {
@@ -337,6 +340,7 @@ pub impl Task {
         return Task {
             current_stack_segment: stack,
             saved_context: initial_context,
+            local_services: LocalServices::new()
         };
     }
 

From 2fe118b26fdb2897eb000ae47f912cc6b2534324 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sun, 21 Apr 2013 17:42:45 -0700
Subject: [PATCH 17/28] rt: Don't make memory_region depend on rust_env

I am going to use memory_region and boxed_region as the local heap
in the new scheduler, for now at least, and I don't have a rust_env
available.
---
 src/rt/boxed_region.cpp    |  2 +-
 src/rt/boxed_region.h      |  6 +++---
 src/rt/memory_region.cpp   | 15 +++++++++------
 src/rt/memory_region.h     |  5 +++--
 src/rt/rust_sched_loop.cpp |  2 +-
 src/rt/rust_task.cpp       |  2 +-
 6 files changed, 18 insertions(+), 14 deletions(-)

diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp
index d159df03dc3..e8ddb53148c 100644
--- a/src/rt/boxed_region.cpp
+++ b/src/rt/boxed_region.cpp
@@ -82,7 +82,7 @@ void boxed_region::free(rust_opaque_box *box) {
     if (box->next) box->next->prev = box->prev;
     if (live_allocs == box) live_allocs = box->next;
 
-    if (env->poison_on_free) {
+    if (poison_on_free) {
         memset(box_body(box), 0xab, box->td->size);
     }
 
diff --git a/src/rt/boxed_region.h b/src/rt/boxed_region.h
index 4097b6d41b7..178772007e5 100644
--- a/src/rt/boxed_region.h
+++ b/src/rt/boxed_region.h
@@ -24,7 +24,7 @@ struct rust_env;
  * a type descr which describes the payload (what follows the header). */
 class boxed_region {
 private:
-    rust_env *env;
+    bool poison_on_free;
     memory_region *backing_region;
     rust_opaque_box *live_allocs;
 
@@ -41,8 +41,8 @@ private:
     boxed_region& operator=(const boxed_region& rhs);
 
 public:
-    boxed_region(rust_env *e, memory_region *br)
-        : env(e)
+    boxed_region(memory_region *br, bool poison_on_free)
+        : poison_on_free(poison_on_free)
         , backing_region(br)
         , live_allocs(NULL)
     {}
diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp
index 6de9d5a1df4..f3406712cb0 100644
--- a/src/rt/memory_region.cpp
+++ b/src/rt/memory_region.cpp
@@ -11,7 +11,6 @@
 
 #include "sync/sync.h"
 #include "memory_region.h"
-#include "rust_env.h"
 
 #if RUSTRT_TRACK_ALLOCATIONS >= 3
 #include <execinfo.h>
@@ -35,15 +34,19 @@ void *memory_region::get_data(alloc_header *ptr) {
     return (void*)((char *)ptr + HEADER_SIZE);
 }
 
-memory_region::memory_region(rust_env *env, bool synchronized) :
-    _env(env), _parent(NULL), _live_allocations(0),
-    _detailed_leaks(env->detailed_leaks),
+memory_region::memory_region(bool synchronized,
+                             bool detailed_leaks,
+                             bool poison_on_free) :
+    _parent(NULL), _live_allocations(0),
+    _detailed_leaks(detailed_leaks),
+    _poison_on_free(poison_on_free),
     _synchronized(synchronized) {
 }
 
 memory_region::memory_region(memory_region *parent) :
-    _env(parent->_env), _parent(parent), _live_allocations(0),
+    _parent(parent), _live_allocations(0),
     _detailed_leaks(parent->_detailed_leaks),
+    _poison_on_free(parent->_poison_on_free),
     _synchronized(parent->_synchronized) {
 }
 
@@ -241,7 +244,7 @@ memory_region::claim_alloc(void *mem) {
 void
 memory_region::maybe_poison(void *mem) {
 
-    if (!_env->poison_on_free)
+    if (!_poison_on_free)
         return;
 
 #   if RUSTRT_TRACK_ALLOCATIONS >= 1
diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h
index 999a992eefa..4ad57c11809 100644
--- a/src/rt/memory_region.h
+++ b/src/rt/memory_region.h
@@ -54,11 +54,11 @@ private:
     inline alloc_header *get_header(void *mem);
     inline void *get_data(alloc_header *);
 
-    rust_env *_env;
     memory_region *_parent;
     int _live_allocations;
     array_list<alloc_header *> _allocation_list;
     const bool _detailed_leaks;
+    const bool _poison_on_free;
     const bool _synchronized;
     lock_and_signal _lock;
 
@@ -75,7 +75,8 @@ private:
     memory_region& operator=(const memory_region& rhs);
 
 public:
-    memory_region(rust_env *env, bool synchronized);
+    memory_region(bool synchronized,
+                  bool detailed_leaks, bool poison_on_free);
     memory_region(memory_region *parent);
     void *malloc(size_t size, const char *tag);
     void *realloc(void *mem, size_t size);
diff --git a/src/rt/rust_sched_loop.cpp b/src/rt/rust_sched_loop.cpp
index 90393acdd59..e7ce4b08ee4 100644
--- a/src/rt/rust_sched_loop.cpp
+++ b/src/rt/rust_sched_loop.cpp
@@ -36,7 +36,7 @@ rust_sched_loop::rust_sched_loop(rust_scheduler *sched, int id, bool killed) :
     sched(sched),
     log_lvl(log_debug),
     min_stack_size(kernel->env->min_stack_size),
-    local_region(kernel->env, false),
+    local_region(false, kernel->env->detailed_leaks, kernel->env->poison_on_free),
     // FIXME #2891: calculate a per-scheduler name.
     name("main")
 {
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 63dc1c9833e..4680f32c19a 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -36,7 +36,7 @@ rust_task::rust_task(rust_sched_loop *sched_loop, rust_task_state state,
     kernel(sched_loop->kernel),
     name(name),
     list_index(-1),
-    boxed(sched_loop->kernel->env, &local_region),
+    boxed(&local_region, sched_loop->kernel->env->poison_on_free),
     local_region(&sched_loop->local_region),
     unwinding(false),
     total_stack_sz(0),

From d7f5e437a28dd85b8a7523af9212a9a1100ea725 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Sun, 21 Apr 2013 19:03:52 -0700
Subject: [PATCH 18/28] core::rt: Add the local heap to newsched tasks

Reusing the existing boxed_region implementation from the runtime
---
 src/libcore/rt/local_heap.rs     | 81 ++++++++++++++++++++++++++++++++
 src/libcore/rt/local_services.rs | 19 +++++++-
 src/libcore/rt/mod.rs            |  1 +
 src/libcore/unstable/lang.rs     | 26 +++++++++-
 src/rt/boxed_region.cpp          | 12 ++---
 src/rt/rust_builtin.cpp          | 35 ++++++++++++++
 src/rt/rustrt.def.in             |  6 +++
 7 files changed, 170 insertions(+), 10 deletions(-)
 create mode 100644 src/libcore/rt/local_heap.rs

diff --git a/src/libcore/rt/local_heap.rs b/src/libcore/rt/local_heap.rs
new file mode 100644
index 00000000000..fbd4a77d79b
--- /dev/null
+++ b/src/libcore/rt/local_heap.rs
@@ -0,0 +1,81 @@
+// Copyright 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.
+
+//! The local, garbage collected heap
+
+use libc::{c_void, uintptr_t, size_t};
+use ops::Drop;
+
+type MemoryRegion = c_void;
+type BoxedRegion = c_void;
+
+pub type OpaqueBox = c_void;
+pub type TypeDesc = c_void;
+
+pub struct LocalHeap {
+    memory_region: *MemoryRegion,
+    boxed_region: *BoxedRegion
+}
+
+impl LocalHeap {
+    pub fn new() -> LocalHeap {
+        unsafe {
+            // Don't need synchronization for the single-threaded local heap
+            let synchronized = false as uintptr_t;
+            // XXX: These usually come from the environment
+            let detailed_leaks = false as uintptr_t;
+            let poison_on_free = false as uintptr_t;
+            let region = rust_new_memory_region(synchronized, detailed_leaks, poison_on_free);
+            assert!(region.is_not_null());
+            let boxed = rust_new_boxed_region(region, poison_on_free);
+            assert!(boxed.is_not_null());
+            LocalHeap {
+                memory_region: region,
+                boxed_region: boxed
+            }
+        }
+    }
+
+    pub fn alloc(&mut self, td: *TypeDesc, size: uint) -> *OpaqueBox {
+        unsafe {
+            return rust_boxed_region_malloc(self.boxed_region, td, size as size_t);
+        }
+    }
+
+    pub fn free(&mut self, box: *OpaqueBox) {
+        unsafe {
+            return rust_boxed_region_free(self.boxed_region, box);
+        }
+    }
+}
+
+impl Drop for LocalHeap {
+    fn finalize(&self) {
+        unsafe {
+            rust_delete_boxed_region(self.boxed_region);
+            rust_delete_memory_region(self.memory_region);
+        }
+    }
+}
+
+extern {
+    fn rust_new_memory_region(synchronized: uintptr_t,
+                               detailed_leaks: uintptr_t,
+                               poison_on_free: uintptr_t) -> *MemoryRegion;
+    fn rust_delete_memory_region(region: *MemoryRegion);
+    fn rust_new_boxed_region(region: *MemoryRegion,
+                             poison_on_free: uintptr_t) -> *BoxedRegion;
+    fn rust_delete_boxed_region(region: *BoxedRegion);
+    fn rust_boxed_region_malloc(region: *BoxedRegion,
+                                td: *TypeDesc,
+                                size: size_t) -> *OpaqueBox;
+    fn rust_boxed_region_free(region: *BoxedRegion, box: *OpaqueBox);
+}
+
diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
index 8e386f80b77..b9d99283e14 100644
--- a/src/libcore/rt/local_services.rs
+++ b/src/libcore/rt/local_services.rs
@@ -20,6 +20,7 @@
 
 use prelude::*;
 use super::sched::{Task, local_sched};
+use super::local_heap::LocalHeap;
 
 pub struct LocalServices {
     heap: LocalHeap,
@@ -29,7 +30,6 @@ pub struct LocalServices {
     unwinder: Unwinder
 }
 
-pub struct LocalHeap;
 pub struct GarbageCollector;
 pub struct LocalStorage;
 pub struct Logger;
@@ -38,7 +38,7 @@ pub struct Unwinder;
 impl LocalServices {
     pub fn new() -> LocalServices {
         LocalServices {
-            heap: LocalHeap,
+            heap: LocalHeap::new(),
             gc: GarbageCollector,
             storage: LocalStorage,
             logger: Logger,
@@ -61,3 +61,18 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
         }
     }
 }
+
+#[cfg(test)]
+mod test {
+    use rt::test::*;
+
+    #[test]
+    fn local_heap() {
+        do run_in_newsched_task() {
+            let a = @5;
+            let b = a;
+            assert!(*a == 5);
+            assert!(*b == 5);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs
index e0190418b13..4a767d61f74 100644
--- a/src/libcore/rt/mod.rs
+++ b/src/libcore/rt/mod.rs
@@ -49,6 +49,7 @@ mod context;
 mod thread;
 pub mod env;
 pub mod local_services;
+mod local_heap;
 
 /// Tools for testing the runtime
 #[cfg(test)]
diff --git a/src/libcore/unstable/lang.rs b/src/libcore/unstable/lang.rs
index be776a39742..0bf1ad36a1f 100644
--- a/src/libcore/unstable/lang.rs
+++ b/src/libcore/unstable/lang.rs
@@ -17,6 +17,8 @@ use str;
 use sys;
 use unstable::exchange_alloc;
 use cast::transmute;
+use rt::{context, OldTaskContext};
+use rt::local_services::borrow_local_services;
 
 #[allow(non_camel_case_types)]
 pub type rust_task = c_void;
@@ -81,7 +83,18 @@ pub unsafe fn exchange_free(ptr: *c_char) {
 #[lang="malloc"]
 #[inline(always)]
 pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
-    return rustrt::rust_upcall_malloc(td, size);
+    match context() {
+        OldTaskContext => {
+            return rustrt::rust_upcall_malloc(td, size);
+        }
+        _ => {
+            let mut alloc = ::ptr::null();
+            do borrow_local_services |srv| {
+                alloc = srv.heap.alloc(td as *c_void, size as uint) as *c_char;
+            }
+            return alloc;
+        }
+    }
 }
 
 // NB: Calls to free CANNOT be allowed to fail, as throwing an exception from
@@ -90,7 +103,16 @@ pub unsafe fn local_malloc(td: *c_char, size: uintptr_t) -> *c_char {
 #[lang="free"]
 #[inline(always)]
 pub unsafe fn local_free(ptr: *c_char) {
-    rustrt::rust_upcall_free(ptr);
+    match context() {
+        OldTaskContext => {
+            rustrt::rust_upcall_free(ptr);
+        }
+        _ => {
+            do borrow_local_services |srv| {
+                srv.heap.free(ptr as *c_void);
+            }
+        }
+    }
 }
 
 #[lang="borrow_as_imm"]
diff --git a/src/rt/boxed_region.cpp b/src/rt/boxed_region.cpp
index e8ddb53148c..a49b52bffe1 100644
--- a/src/rt/boxed_region.cpp
+++ b/src/rt/boxed_region.cpp
@@ -27,11 +27,11 @@ rust_opaque_box *boxed_region::malloc(type_desc *td, size_t body_size) {
     if (live_allocs) live_allocs->prev = box;
     live_allocs = box;
 
-    LOG(rust_get_current_task(), box,
+    /*LOG(rust_get_current_task(), box,
         "@malloc()=%p with td %p, size %lu==%lu+%lu, "
         "align %lu, prev %p, next %p\n",
         box, td, total_size, sizeof(rust_opaque_box), body_size,
-        td->align, box->prev, box->next);
+        td->align, box->prev, box->next);*/
 
     return box;
 }
@@ -50,9 +50,9 @@ rust_opaque_box *boxed_region::realloc(rust_opaque_box *box,
     if (new_box->next) new_box->next->prev = new_box;
     if (live_allocs == box) live_allocs = new_box;
 
-    LOG(rust_get_current_task(), box,
+    /*LOG(rust_get_current_task(), box,
         "@realloc()=%p with orig=%p, size %lu==%lu+%lu",
-        new_box, box, total_size, sizeof(rust_opaque_box), new_size);
+        new_box, box, total_size, sizeof(rust_opaque_box), new_size);*/
 
     return new_box;
 }
@@ -74,9 +74,9 @@ void boxed_region::free(rust_opaque_box *box) {
     // double frees (kind of).
     assert(box->td != NULL);
 
-    LOG(rust_get_current_task(), box,
+    /*LOG(rust_get_current_task(), box,
         "@free(%p) with td %p, prev %p, next %p\n",
-        box, box->td, box->prev, box->next);
+        box, box->td, box->prev, box->next);*/
 
     if (box->prev) box->prev->next = box->next;
     if (box->next) box->next->prev = box->prev;
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index a0db6f64f69..b8749b8f73d 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -851,6 +851,41 @@ rust_initialize_global_state() {
     }
 }
 
+extern "C" CDECL memory_region*
+rust_new_memory_region(uintptr_t synchronized,
+                       uintptr_t detailed_leaks,
+                       uintptr_t poison_on_free) {
+    return new memory_region((bool)synchronized,
+                             (bool)detailed_leaks,
+                             (bool)poison_on_free);
+}
+
+extern "C" CDECL void
+rust_delete_memory_region(memory_region *region) {
+    delete region;
+}
+
+extern "C" CDECL boxed_region*
+rust_new_boxed_region(memory_region *region,
+                      uintptr_t poison_on_free) {
+    return new boxed_region(region, poison_on_free);
+}
+
+extern "C" CDECL void
+rust_delete_boxed_region(boxed_region *region) {
+    delete region;
+}
+
+extern "C" CDECL rust_opaque_box*
+rust_boxed_region_malloc(boxed_region *region, type_desc *td, size_t size) {
+    return region->malloc(td, size);
+}
+
+extern "C" CDECL void
+rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) {
+    region->free(box);
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index f84a73a394b..9aa80156783 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -222,3 +222,9 @@ rust_uv_free_ip6_addr
 rust_call_nullary_fn
 rust_initialize_global_state
 rust_dbg_next_port
+rust_new_memory_region
+rust_delete_memory_region
+rust_new_boxed_region
+rust_delete_boxed_region
+rust_boxed_region_malloc
+rust_boxed_region_free

From d0786fdffcbae5c89762455fd3b3ffb5b9a3b6a1 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 12:54:03 -0700
Subject: [PATCH 19/28] core::rt Wire up task-local storage to the new
 scheduler

---
 src/libcore/rt/local_services.rs    | 58 +++++++++++++++++--
 src/libcore/rt/sched/mod.rs         |  7 +++
 src/libcore/task/local_data.rs      | 10 ++--
 src/libcore/task/local_data_priv.rs | 87 ++++++++++++++++++++++++-----
 src/libcore/task/spawn.rs           |  8 +--
 5 files changed, 144 insertions(+), 26 deletions(-)

diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
index b9d99283e14..d29e57a17af 100644
--- a/src/libcore/rt/local_services.rs
+++ b/src/libcore/rt/local_services.rs
@@ -19,6 +19,7 @@
 //! (freestanding rust with local services?).
 
 use prelude::*;
+use libc::c_void;
 use super::sched::{Task, local_sched};
 use super::local_heap::LocalHeap;
 
@@ -27,11 +28,12 @@ pub struct LocalServices {
     gc: GarbageCollector,
     storage: LocalStorage,
     logger: Logger,
-    unwinder: Unwinder
+    unwinder: Unwinder,
+    destroyed: bool
 }
 
 pub struct GarbageCollector;
-pub struct LocalStorage;
+pub struct LocalStorage(*c_void, Option<~fn(*c_void)>);
 pub struct Logger;
 pub struct Unwinder;
 
@@ -40,11 +42,34 @@ impl LocalServices {
         LocalServices {
             heap: LocalHeap::new(),
             gc: GarbageCollector,
-            storage: LocalStorage,
+            storage: LocalStorage(ptr::null(), None),
             logger: Logger,
-            unwinder: Unwinder
+            unwinder: Unwinder,
+            destroyed: false
         }
     }
+
+    /// Must be called manually before finalization to clean up
+    /// thread-local resources. Some of the routines here expect
+    /// LocalServices to be available recursively so this must be
+    /// called unsafely, without removing LocalServices from
+    /// thread-local-storage.
+    pub fn destroy(&mut self) {
+        // This is just an assertion that `destroy` was called unsafely
+        // and this instance of LocalServices is still accessible.
+        do borrow_local_services |sched| {
+            assert!(ptr::ref_eq(sched, self));
+        }
+        match self.storage {
+            LocalStorage(ptr, Some(ref dtor)) => (*dtor)(ptr),
+            _ => ()
+        }
+        self.destroyed = true;
+    }
+}
+
+impl Drop for LocalServices {
+    fn finalize(&self) { assert!(self.destroyed) }
 }
 
 /// Borrow a pointer to the installed local services.
@@ -62,6 +87,19 @@ pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
     }
 }
 
+pub unsafe fn unsafe_borrow_local_services() -> &mut LocalServices {
+    use cast::transmute_mut_region;
+
+    match local_sched::unsafe_borrow().current_task {
+        Some(~ref mut task) => {
+            transmute_mut_region(&mut task.local_services)
+        }
+        None => {
+            fail!(~"no local services for schedulers yet")
+        }
+    }
+}
+
 #[cfg(test)]
 mod test {
     use rt::test::*;
@@ -75,4 +113,16 @@ mod test {
             assert!(*b == 5);
         }
     }
+
+    #[test]
+    fn tls() {
+        use task::local_data::*;
+        do run_in_newsched_task() {
+            unsafe {
+                fn key(_x: @~str) { }
+                local_data_set(key, @~"data");
+                assert!(*local_data_get(key).get() == ~"data");
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index 30136e443ee..b7d861b8946 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -357,6 +357,13 @@ pub impl Task {
 
             start();
 
+            unsafe {
+                // Destroy the local heap, TLS, etc.
+                let sched = local_sched::unsafe_borrow();
+                let task = sched.current_task.get_mut_ref();
+                task.local_services.destroy();
+            }
+
             let sched = local_sched::take();
             sched.terminate_current_task();
         };
diff --git a/src/libcore/task/local_data.rs b/src/libcore/task/local_data.rs
index 261671f6de9..6e919a74ed4 100644
--- a/src/libcore/task/local_data.rs
+++ b/src/libcore/task/local_data.rs
@@ -27,7 +27,7 @@ magic.
 */
 
 use prelude::*;
-use task::local_data_priv::{local_get, local_pop, local_modify, local_set};
+use task::local_data_priv::{local_get, local_pop, local_modify, local_set, Handle};
 use task::rt;
 
 /**
@@ -53,7 +53,7 @@ pub type LocalDataKey<'self,T> = &'self fn(v: @T);
 pub unsafe fn local_data_pop<T:Durable>(
     key: LocalDataKey<T>) -> Option<@T> {
 
-    local_pop(rt::rust_get_task(), key)
+    local_pop(Handle::new(), key)
 }
 /**
  * Retrieve a task-local data value. It will also be kept alive in the
@@ -62,7 +62,7 @@ pub unsafe fn local_data_pop<T:Durable>(
 pub unsafe fn local_data_get<T:Durable>(
     key: LocalDataKey<T>) -> Option<@T> {
 
-    local_get(rt::rust_get_task(), key)
+    local_get(Handle::new(), key)
 }
 /**
  * Store a value in task-local data. If this key already has a value,
@@ -71,7 +71,7 @@ pub unsafe fn local_data_get<T:Durable>(
 pub unsafe fn local_data_set<T:Durable>(
     key: LocalDataKey<T>, data: @T) {
 
-    local_set(rt::rust_get_task(), key, data)
+    local_set(Handle::new(), key, data)
 }
 /**
  * Modify a task-local data value. If the function returns 'None', the
@@ -81,7 +81,7 @@ pub unsafe fn local_data_modify<T:Durable>(
     key: LocalDataKey<T>,
     modify_fn: &fn(Option<@T>) -> Option<@T>) {
 
-    local_modify(rt::rust_get_task(), key, modify_fn)
+    local_modify(Handle::new(), key, modify_fn)
 }
 
 #[test]
diff --git a/src/libcore/task/local_data_priv.rs b/src/libcore/task/local_data_priv.rs
index 43f5fa4654b..50e8286e738 100644
--- a/src/libcore/task/local_data_priv.rs
+++ b/src/libcore/task/local_data_priv.rs
@@ -18,6 +18,30 @@ use task::rt;
 use task::local_data::LocalDataKey;
 
 use super::rt::rust_task;
+use rt::local_services::LocalStorage;
+
+pub enum Handle {
+    OldHandle(*rust_task),
+    NewHandle(*mut LocalStorage)
+}
+
+impl Handle {
+    pub fn new() -> Handle {
+        use rt::{context, OldTaskContext};
+        use rt::local_services::unsafe_borrow_local_services;
+        unsafe {
+            match context() {
+                OldTaskContext => {
+                    OldHandle(rt::rust_get_task())
+                }
+                _ => {
+                    let local_services = unsafe_borrow_local_services();
+                    NewHandle(&mut local_services.storage)
+                }
+            }
+        }
+    }
+}
 
 pub trait LocalData { }
 impl<T:Durable> LocalData for @T { }
@@ -39,7 +63,7 @@ type TaskLocalElement = (*libc::c_void, *libc::c_void, @LocalData);
 // Has to be a pointer at outermost layer; the foreign call returns void *.
 type TaskLocalMap = @mut ~[Option<TaskLocalElement>];
 
-extern fn cleanup_task_local_map(map_ptr: *libc::c_void) {
+fn cleanup_task_local_map(map_ptr: *libc::c_void) {
     unsafe {
         assert!(!map_ptr.is_null());
         // Get and keep the single reference that was created at the
@@ -50,8 +74,19 @@ extern fn cleanup_task_local_map(map_ptr: *libc::c_void) {
 }
 
 // Gets the map from the runtime. Lazily initialises if not done so already.
+unsafe fn get_local_map(handle: Handle) -> TaskLocalMap {
+    match handle {
+        OldHandle(task) => get_task_local_map(task),
+        NewHandle(local_storage) => get_newsched_local_map(local_storage)
+    }
+}
+
 unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap {
 
+    extern fn cleanup_task_local_map_(map_ptr: *libc::c_void) {
+        cleanup_task_local_map(map_ptr);
+    }
+
     // Relies on the runtime initialising the pointer to null.
     // Note: The map's box lives in TLS invisibly referenced once. Each time
     // we retrieve it for get/set, we make another reference, which get/set
@@ -62,7 +97,7 @@ unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap {
         // Use reinterpret_cast -- transmute would take map away from us also.
         rt::rust_set_task_local_data(
             task, cast::reinterpret_cast(&map));
-        rt::rust_task_local_data_atexit(task, cleanup_task_local_map);
+        rt::rust_task_local_data_atexit(task, cleanup_task_local_map_);
         // Also need to reference it an extra time to keep it for now.
         let nonmut = cast::transmute::<TaskLocalMap,
                                        @~[Option<TaskLocalElement>]>(map);
@@ -77,6 +112,32 @@ unsafe fn get_task_local_map(task: *rust_task) -> TaskLocalMap {
     }
 }
 
+unsafe fn get_newsched_local_map(local: *mut LocalStorage) -> TaskLocalMap {
+    match &mut *local {
+        &LocalStorage(map_ptr, Some(_)) => {
+            assert!(map_ptr.is_not_null());
+            let map = cast::transmute(map_ptr);
+            let nonmut = cast::transmute::<TaskLocalMap,
+            @~[Option<TaskLocalElement>]>(map);
+            cast::bump_box_refcount(nonmut);
+            return map;
+        }
+        &LocalStorage(ref mut map_ptr, ref mut at_exit) => {
+            assert!((*map_ptr).is_null());
+            let map: TaskLocalMap = @mut ~[];
+            // Use reinterpret_cast -- transmute would take map away from us also.
+            *map_ptr = cast::reinterpret_cast(&map);
+            let at_exit_fn: ~fn(*libc::c_void) = |p|cleanup_task_local_map(p);
+            *at_exit = Some(at_exit_fn);
+            // Also need to reference it an extra time to keep it for now.
+            let nonmut = cast::transmute::<TaskLocalMap,
+            @~[Option<TaskLocalElement>]>(map);
+            cast::bump_box_refcount(nonmut);
+            return map;
+        }
+    }
+}
+
 unsafe fn key_to_key_value<T:Durable>(
     key: LocalDataKey<T>) -> *libc::c_void {
 
@@ -106,10 +167,10 @@ unsafe fn local_data_lookup<T:Durable>(
 }
 
 unsafe fn local_get_helper<T:Durable>(
-    task: *rust_task, key: LocalDataKey<T>,
+    handle: Handle, key: LocalDataKey<T>,
     do_pop: bool) -> Option<@T> {
 
-    let map = get_task_local_map(task);
+    let map = get_local_map(handle);
     // Interpreturn our findings from the map
     do local_data_lookup(map, key).map |result| {
         // A reference count magically appears on 'data' out of thin air. It
@@ -128,23 +189,23 @@ unsafe fn local_get_helper<T:Durable>(
 
 
 pub unsafe fn local_pop<T:Durable>(
-    task: *rust_task,
+    handle: Handle,
     key: LocalDataKey<T>) -> Option<@T> {
 
-    local_get_helper(task, key, true)
+    local_get_helper(handle, key, true)
 }
 
 pub unsafe fn local_get<T:Durable>(
-    task: *rust_task,
+    handle: Handle,
     key: LocalDataKey<T>) -> Option<@T> {
 
-    local_get_helper(task, key, false)
+    local_get_helper(handle, key, false)
 }
 
 pub unsafe fn local_set<T:Durable>(
-    task: *rust_task, key: LocalDataKey<T>, data: @T) {
+    handle: Handle, key: LocalDataKey<T>, data: @T) {
 
-    let map = get_task_local_map(task);
+    let map = get_local_map(handle);
     // Store key+data as *voids. Data is invisibly referenced once; key isn't.
     let keyval = key_to_key_value(key);
     // We keep the data in two forms: one as an unsafe pointer, so we can get
@@ -174,12 +235,12 @@ pub unsafe fn local_set<T:Durable>(
 }
 
 pub unsafe fn local_modify<T:Durable>(
-    task: *rust_task, key: LocalDataKey<T>,
+    handle: Handle, key: LocalDataKey<T>,
     modify_fn: &fn(Option<@T>) -> Option<@T>) {
 
     // Could be more efficient by doing the lookup work, but this is easy.
-    let newdata = modify_fn(local_pop(task, key));
+    let newdata = modify_fn(local_pop(handle, key));
     if newdata.is_some() {
-        local_set(task, key, newdata.unwrap());
+        local_set(handle, key, newdata.unwrap());
     }
 }
diff --git a/src/libcore/task/spawn.rs b/src/libcore/task/spawn.rs
index 118c4cc2312..1e3857dff9a 100644
--- a/src/libcore/task/spawn.rs
+++ b/src/libcore/task/spawn.rs
@@ -80,7 +80,7 @@ use prelude::*;
 use unstable;
 use ptr;
 use hashmap::HashSet;
-use task::local_data_priv::{local_get, local_set};
+use task::local_data_priv::{local_get, local_set, OldHandle};
 use task::rt::rust_task;
 use task::rt;
 use task::{Failure, ManualThreads, PlatformThread, SchedOpts, SingleThreaded};
@@ -451,7 +451,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
         /*##################################################################*
          * Step 1. Get spawner's taskgroup info.
          *##################################################################*/
-        let spawner_group = match local_get(spawner, taskgroup_key!()) {
+        let spawner_group = match local_get(OldHandle(spawner), taskgroup_key!()) {
             None => {
                 // Main task, doing first spawn ever. Lazily initialise here.
                 let mut members = new_taskset();
@@ -463,7 +463,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
                 // Main task/group has no ancestors, no notifier, etc.
                 let group =
                     @TCB(spawner, tasks, AncestorList(None), true, None);
-                local_set(spawner, taskgroup_key!(), group);
+                local_set(OldHandle(spawner), taskgroup_key!(), group);
                 group
             }
             Some(group) => group
@@ -627,7 +627,7 @@ fn spawn_raw_oldsched(opts: TaskOpts, f: ~fn()) {
                 let group = @TCB(child, child_arc, ancestors,
                                  is_main, notifier);
                 unsafe {
-                    local_set(child, taskgroup_key!(), group);
+                    local_set(OldHandle(child), taskgroup_key!(), group);
                 }
 
                 // Run the child's body.

From e5d21b9ff1ea4160b728b62aeca110c0a563d9ee Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 13:10:59 -0700
Subject: [PATCH 20/28] core::rt: Make I/O constructors return Option instead
 of Result

For consistency, for all I/O calls, inspecting the error can be done with
the io_error condition.
---
 src/libcore/rt/io/file.rs     | 2 +-
 src/libcore/rt/io/net/tcp.rs  | 4 ++--
 src/libcore/rt/io/net/udp.rs  | 4 ++--
 src/libcore/rt/io/net/unix.rs | 4 ++--
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/libcore/rt/io/file.rs b/src/libcore/rt/io/file.rs
index 4e3e01a5ece..e4fe066a173 100644
--- a/src/libcore/rt/io/file.rs
+++ b/src/libcore/rt/io/file.rs
@@ -42,7 +42,7 @@ impl FileStream {
     pub fn open<P: PathLike>(_path: &P,
                              _mode: FileMode,
                              _access: FileAccess
-                            ) -> Result<FileStream, IoError> {
+                            ) -> Option<FileStream> {
         fail!()
     }
 }
diff --git a/src/libcore/rt/io/net/tcp.rs b/src/libcore/rt/io/net/tcp.rs
index d78241b8e44..d726bae821c 100644
--- a/src/libcore/rt/io/net/tcp.rs
+++ b/src/libcore/rt/io/net/tcp.rs
@@ -16,7 +16,7 @@ use super::ip::IpAddr;
 pub struct TcpStream;
 
 impl TcpStream {
-    pub fn connect(_addr: IpAddr) -> Result<TcpStream, IoError> {
+    pub fn connect(_addr: IpAddr) -> Option<TcpStream> {
         fail!()
     }
 }
@@ -40,7 +40,7 @@ impl Close for TcpStream {
 pub struct TcpListener;
 
 impl TcpListener {
-    pub fn bind(_addr: IpAddr) -> Result<TcpListener, IoError> {
+    pub fn bind(_addr: IpAddr) -> Option<TcpListener> {
         fail!()
     }
 }
diff --git a/src/libcore/rt/io/net/udp.rs b/src/libcore/rt/io/net/udp.rs
index 81a6677c14a..8691a697e88 100644
--- a/src/libcore/rt/io/net/udp.rs
+++ b/src/libcore/rt/io/net/udp.rs
@@ -16,7 +16,7 @@ use super::ip::IpAddr;
 pub struct UdpStream;
 
 impl UdpStream {
-    pub fn connect(_addr: IpAddr) -> Result<UdpStream, IoError> {
+    pub fn connect(_addr: IpAddr) -> Option<UdpStream> {
         fail!()
     }
 }
@@ -40,7 +40,7 @@ impl Close for UdpStream {
 pub struct UdpListener;
 
 impl UdpListener {
-    pub fn bind(_addr: IpAddr) -> Result<UdpListener, IoError> {
+    pub fn bind(_addr: IpAddr) -> Option<UdpListener> {
         fail!()
     }
 }
diff --git a/src/libcore/rt/io/net/unix.rs b/src/libcore/rt/io/net/unix.rs
index a5f4f8e3ba8..bb3db6ec0d5 100644
--- a/src/libcore/rt/io/net/unix.rs
+++ b/src/libcore/rt/io/net/unix.rs
@@ -16,7 +16,7 @@ use super::super::support::PathLike;
 pub struct UnixStream;
 
 impl UnixStream {
-    pub fn connect<P: PathLike>(_path: &P) -> Result<UnixStream, IoError> {
+    pub fn connect<P: PathLike>(_path: &P) -> Option<UnixStream> {
         fail!()
     }
 }
@@ -40,7 +40,7 @@ impl Close for UnixStream {
 pub struct UnixListener;
 
 impl UnixListener {
-    pub fn bind<P: PathLike>(_path: &P) -> Result<UnixListener, IoError> {
+    pub fn bind<P: PathLike>(_path: &P) -> Option<UnixListener> {
         fail!()
     }
 }

From 6644a034f0650c638eea8809a5f035ffaca0de88 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 13:26:37 -0700
Subject: [PATCH 21/28] core::rt: Move the definition of Listener to rt::io

---
 src/libcore/rt/io/mod.rs     | 24 +++++++++++++++++++++---
 src/libcore/rt/io/net/mod.rs | 31 -------------------------------
 2 files changed, 21 insertions(+), 34 deletions(-)
 delete mode 100644 src/libcore/rt/io/net/mod.rs

diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index 238bd97a62d..f1b248c6e1d 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -122,7 +122,6 @@ pub use self::stdio::print;
 pub use self::stdio::println;
 
 pub use self::file::FileStream;
-pub use self::net::Listener;
 pub use self::net::ip::IpAddr;
 pub use self::net::tcp::TcpListener;
 pub use self::net::tcp::TcpStream;
@@ -137,8 +136,14 @@ pub use self::extensions::WriterByteConversions;
 pub mod file;
 
 /// Synchronous, non-blocking network I/O.
-#[path = "net/mod.rs"]
-pub mod net;
+pub mod net {
+    pub mod tcp;
+    pub mod udp;
+    pub mod ip;
+    #[cfg(unix)]
+    pub mod unix;
+    pub mod http;
+}
 
 /// Readers and Writers for memory buffers and strings.
 #[cfg(not(stage0))] // XXX Using unsnapshotted features
@@ -280,6 +285,19 @@ pub trait Seek {
     fn seek(&mut self, pos: i64, style: SeekStyle);
 }
 
+/// A listener is a value that listens for connections
+pub trait Listener<S> {
+    /// Wait for and accept an incoming connection
+    ///
+    /// Returns `None` on timeout.
+    ///
+    /// # Failure
+    ///
+    /// Raises `io_error` condition. If the condition is handled,
+    /// then `accept` returns `None`.
+    fn accept(&mut self) -> Option<S>;
+}
+
 /// Common trait for decorator types.
 ///
 /// Provides accessors to get the inner, 'decorated' values. The I/O library
diff --git a/src/libcore/rt/io/net/mod.rs b/src/libcore/rt/io/net/mod.rs
deleted file mode 100644
index 130ff6b38fa..00000000000
--- a/src/libcore/rt/io/net/mod.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Copyright 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.
-
-use prelude::*;
-
-pub mod tcp;
-pub mod udp;
-pub mod ip;
-#[cfg(unix)]
-pub mod unix;
-pub mod http;
-
-/// A listener is a value that listens for connections
-pub trait Listener<S> {
-    /// Wait for and accept an incoming connection
-    ///
-    /// Returns `None` on timeout.
-    ///
-    /// # Failure
-    ///
-    /// Raises `io_error` condition. If the condition is handled,
-    /// then `accept` returns `None`.
-    fn accept(&mut self) -> Option<S>;
-}

From fe13b865192028645b50c17d2cb1a6d44481f338 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 14:43:02 -0700
Subject: [PATCH 22/28] Make conditions public. #6009

---
 src/libcore/condition.rs    | 21 +++++++++++++++++++++
 src/libsyntax/ext/expand.rs |  2 +-
 2 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/src/libcore/condition.rs b/src/libcore/condition.rs
index dc6c80228dd..c639239981c 100644
--- a/src/libcore/condition.rs
+++ b/src/libcore/condition.rs
@@ -192,4 +192,25 @@ mod test {
 
         assert!(trapped);
     }
+
+    // Issue #6009
+    mod m {
+        condition! {
+            sadness: int -> int;
+        }
+
+        mod n {
+            use super::sadness;
+
+            #[test]
+            fn test_conditions_are_public() {
+                let mut trapped = false;
+                do sadness::cond.trap(|_| {
+                    0
+                }).in {
+                    sadness::cond.raise(0);
+                }
+            }
+        }
+    }
 }
diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs
index 430402a8982..5c5817d480d 100644
--- a/src/libsyntax/ext/expand.rs
+++ b/src/libsyntax/ext/expand.rs
@@ -475,7 +475,7 @@ pub fn core_macros() -> ~str {
 
         { $c:ident: $in:ty -> $out:ty; } => {
 
-            mod $c {
+            pub mod $c {
                 fn key(_x: @::core::condition::Handler<$in,$out>) { }
 
                 pub static cond :

From 5fbb0949a53a6ac51c6d9b187ef4c464e52ae536 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 14:52:40 -0700
Subject: [PATCH 23/28] core::rt: Add implementations of Reader, Writer, and
 Listener for Option

These will make it easier to write I/O code without worrying about errors
---
 src/libcore/rt/io/mod.rs    |  24 +++++-
 src/libcore/rt/io/option.rs | 153 ++++++++++++++++++++++++++++++++++++
 2 files changed, 174 insertions(+), 3 deletions(-)
 create mode 100644 src/libcore/rt/io/option.rs

diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index f1b248c6e1d..d9d6622277f 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -152,6 +152,9 @@ pub mod mem;
 /// Non-blocking access to stdin, stdout, stderr
 pub mod stdio;
 
+/// Implementations for Option
+mod option;
+
 /// Basic stream compression. XXX: Belongs with other flate code
 #[cfg(not(stage0))] // XXX Using unsnapshotted features
 pub mod flate;
@@ -194,12 +197,14 @@ pub struct IoError {
     detail: Option<~str>
 }
 
+#[deriving(Eq)]
 pub enum IoErrorKind {
     FileNotFound,
     FilePermission,
     ConnectionFailed,
     Closed,
-    OtherIoError
+    OtherIoError,
+    PreviousIoError
 }
 
 // XXX: Can't put doc comments on macros
@@ -232,9 +237,9 @@ pub trait Reader {
     ///         println(reader.read_line());
     ///     }
     ///
-    /// # XXX
+    /// # Failue
     ///
-    /// What does this return if the Reader is in an error state?
+    /// Returns `true` on failure.
     fn eof(&mut self) -> bool;
 }
 
@@ -323,3 +328,16 @@ pub trait Decorator<T> {
     /// Take a mutable reference to the decorated value
     fn inner_mut_ref<'a>(&'a mut self) -> &'a mut T;
 }
+
+pub fn standard_error(kind: IoErrorKind) -> IoError {
+    match kind {
+        PreviousIoError => {
+            IoError {
+                kind: PreviousIoError,
+                desc: "Failing due to a previous I/O error",
+                detail: None
+            }
+        }
+        _ => fail!()
+    }
+}
diff --git a/src/libcore/rt/io/option.rs b/src/libcore/rt/io/option.rs
new file mode 100644
index 00000000000..95f8711cb5b
--- /dev/null
+++ b/src/libcore/rt/io/option.rs
@@ -0,0 +1,153 @@
+// Copyright 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.
+
+//! Implementations of I/O traits for the Option type
+//!
+//! I/O constructors return option types to allow errors to be handled.
+//! These implementations allow e.g. `Option<FileStream>` to be used
+//! as a `Reader` without unwrapping the option first.
+//!
+//! # XXX Seek and Close
+
+use option::*;
+use super::{Reader, Writer, Listener};
+use super::{standard_error, PreviousIoError, io_error, IoError};
+
+fn prev_io_error() -> IoError {
+    standard_error(PreviousIoError)
+}
+
+impl<W: Writer> Writer for Option<W> {
+    fn write(&mut self, buf: &[u8]) {
+        match *self {
+            Some(ref mut writer) => writer.write(buf),
+            None => io_error::cond.raise(prev_io_error())
+        }
+    }
+
+    fn flush(&mut self) {
+        match *self {
+            Some(ref mut writer) => writer.flush(),
+            None => io_error::cond.raise(prev_io_error())
+        }
+    }
+}
+
+impl<R: Reader> Reader for Option<R> {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match *self {
+            Some(ref mut reader) => reader.read(buf),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                None
+            }
+        }
+    }
+
+    fn eof(&mut self) -> bool {
+        match *self {
+            Some(ref mut reader) => reader.eof(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                true
+            }
+        }
+    }
+}
+
+impl<L: Listener<S>, S> Listener<S> for Option<L> {
+    fn accept(&mut self) -> Option<S> {
+        match *self {
+            Some(ref mut listener) => listener.accept(),
+            None => {
+                io_error::cond.raise(prev_io_error());
+                None
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use option::*;
+    use super::super::mem::*;
+    use rt::test::*;
+    use super::super::{PreviousIoError, io_error};
+
+    #[test]
+    fn test_option_writer() {
+        do run_in_newsched_task {
+            let mut writer: Option<MemWriter> = Some(MemWriter::new());
+            writer.write([0, 1, 2]);
+            writer.flush();
+            assert!(writer.unwrap().inner() == ~[0, 1, 2]);
+        }
+    }
+
+    #[test]
+    fn test_option_writer_error() {
+        do run_in_newsched_task {
+            let mut writer: Option<MemWriter> = None;
+
+            let mut called = false;
+            do io_error::cond.trap(|err| {
+                assert!(err.kind == PreviousIoError);
+                called = true;
+            }).in {
+                writer.write([0, 0, 0]);
+            }
+            assert!(called);
+
+            let mut called = false;
+            do io_error::cond.trap(|err| {
+                assert!(err.kind == PreviousIoError);
+                called = true;
+            }).in {
+                writer.flush();
+            }
+            assert!(called);
+        }
+    }
+
+    #[test]
+    fn test_option_reader() {
+        do run_in_newsched_task {
+            let mut reader: Option<MemReader> = Some(MemReader::new(~[0, 1, 2, 3]));
+            let mut buf = [0, 0];
+            reader.read(buf);
+            assert!(buf == [0, 1]);
+            assert!(!reader.eof());
+        }
+    }
+
+    #[test]
+    fn test_option_reader_error() {
+        let mut reader: Option<MemReader> = None;
+        let mut buf = [];
+
+        let mut called = false;
+        do io_error::cond.trap(|err| {
+            assert!(err.kind == PreviousIoError);
+            called = true;
+        }).in {
+            reader.read(buf);
+        }
+        assert!(called);
+
+        let mut called = false;
+        do io_error::cond.trap(|err| {
+            assert!(err.kind == PreviousIoError);
+            called = true;
+        }).in {
+            assert!(reader.eof());
+        }
+        assert!(called);
+    }
+}

From 42c0f88232847e97e6cf3578ef197d1942bba44d Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 17:15:31 -0700
Subject: [PATCH 24/28] core::rt: Add unwinding to newsched tasks

---
 src/libcore/rt/io/mod.rs         |  1 +
 src/libcore/rt/local_services.rs | 78 ++++++++++++++++++++++++++++++--
 src/libcore/rt/sched/mod.rs      |  7 +--
 src/libcore/rt/test.rs           | 41 +++++++++++++++++
 src/libcore/sys.rs               | 25 ++++++++--
 src/libcore/task/mod.rs          | 18 +++++++-
 src/rt/rust_builtin.cpp          | 22 +++++++++
 src/rt/rust_upcall.cpp           | 16 +++++--
 src/rt/rustrt.def.in             |  2 +
 9 files changed, 190 insertions(+), 20 deletions(-)

diff --git a/src/libcore/rt/io/mod.rs b/src/libcore/rt/io/mod.rs
index d9d6622277f..131743305bc 100644
--- a/src/libcore/rt/io/mod.rs
+++ b/src/libcore/rt/io/mod.rs
@@ -153,6 +153,7 @@ pub mod mem;
 pub mod stdio;
 
 /// Implementations for Option
+#[cfg(not(stage0))] // Requires condition! fixes
 mod option;
 
 /// Basic stream compression. XXX: Belongs with other flate code
diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
index d29e57a17af..fc75a256428 100644
--- a/src/libcore/rt/local_services.rs
+++ b/src/libcore/rt/local_services.rs
@@ -19,7 +19,8 @@
 //! (freestanding rust with local services?).
 
 use prelude::*;
-use libc::c_void;
+use libc::{c_void, uintptr_t};
+use cast::transmute;
 use super::sched::{Task, local_sched};
 use super::local_heap::LocalHeap;
 
@@ -35,7 +36,10 @@ pub struct LocalServices {
 pub struct GarbageCollector;
 pub struct LocalStorage(*c_void, Option<~fn(*c_void)>);
 pub struct Logger;
-pub struct Unwinder;
+
+pub struct Unwinder {
+    unwinding: bool,
+}
 
 impl LocalServices {
     pub fn new() -> LocalServices {
@@ -44,17 +48,28 @@ impl LocalServices {
             gc: GarbageCollector,
             storage: LocalStorage(ptr::null(), None),
             logger: Logger,
-            unwinder: Unwinder,
+            unwinder: Unwinder { unwinding: false },
             destroyed: false
         }
     }
 
+    pub fn run(&mut self, f: &fn()) {
+        // This is just an assertion that `run` was called unsafely
+        // and this instance of LocalServices is still accessible.
+        do borrow_local_services |sched| {
+            assert!(ptr::ref_eq(sched, self));
+        }
+
+        self.unwinder.try(f);
+        self.destroy();
+    }
+
     /// Must be called manually before finalization to clean up
     /// thread-local resources. Some of the routines here expect
     /// LocalServices to be available recursively so this must be
     /// called unsafely, without removing LocalServices from
     /// thread-local-storage.
-    pub fn destroy(&mut self) {
+    fn destroy(&mut self) {
         // This is just an assertion that `destroy` was called unsafely
         // and this instance of LocalServices is still accessible.
         do borrow_local_services |sched| {
@@ -72,6 +87,51 @@ impl Drop for LocalServices {
     fn finalize(&self) { assert!(self.destroyed) }
 }
 
+// Just a sanity check to make sure we are catching a Rust-thrown exception
+static UNWIND_TOKEN: uintptr_t = 839147;
+
+impl Unwinder {
+    pub fn try(&mut self, f: &fn()) {
+        use sys::Closure;
+
+        unsafe {
+            let closure: Closure = transmute(f);
+            let code = transmute(closure.code);
+            let env = transmute(closure.env);
+
+            let token = rust_try(try_fn, code, env);
+            assert!(token == 0 || token == UNWIND_TOKEN);
+        }
+
+        extern fn try_fn(code: *c_void, env: *c_void) {
+            unsafe {
+                let closure: Closure = Closure {
+                    code: transmute(code),
+                    env: transmute(env),
+                };
+                let closure: &fn() = transmute(closure);
+                closure();
+            }
+        }
+
+        extern {
+            #[rust_stack]
+            fn rust_try(f: *u8, code: *c_void, data: *c_void) -> uintptr_t;
+        }
+    }
+
+    pub fn begin_unwind(&mut self) -> ! {
+        self.unwinding = true;
+        unsafe {
+            rust_begin_unwind(UNWIND_TOKEN);
+            return transmute(());
+        }
+        extern {
+            fn rust_begin_unwind(token: uintptr_t);
+        }
+    }
+}
+
 /// Borrow a pointer to the installed local services.
 /// Fails (likely aborting the process) if local services are not available.
 pub fn borrow_local_services(f: &fn(&mut LocalServices)) {
@@ -125,4 +185,14 @@ mod test {
             }
         }
     }
+
+    #[test]
+    fn unwind() {
+        do run_in_newsched_task() {
+            let result = spawn_try(||());
+            assert!(result.is_ok());
+            let result = spawn_try(|| fail!());
+            assert!(result.is_err());
+        }
+    }
 }
\ No newline at end of file
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index b7d861b8946..65456c30fee 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -353,15 +353,10 @@ pub impl Task {
             unsafe {
                 let sched = local_sched::unsafe_borrow();
                 sched.run_cleanup_job();
-            }
 
-            start();
-
-            unsafe {
-                // Destroy the local heap, TLS, etc.
                 let sched = local_sched::unsafe_borrow();
                 let task = sched.current_task.get_mut_ref();
-                task.local_services.destroy();
+                task.local_services.run(start);
             }
 
             let sched = local_sched::take();
diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index e394a873fea..f3d73c91bd6 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -8,6 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use result::{Result, Ok, Err};
 use super::io::net::ip::{IpAddr, Ipv4};
 
 /// Creates a new scheduler in a new thread and runs a task in it,
@@ -47,6 +48,46 @@ pub fn spawn_immediately(f: ~fn()) {
     }
 }
 
+/// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
+pub fn spawn_try(f: ~fn()) -> Result<(), ()> {
+    use cell::Cell;
+    use super::sched::*;
+    use task;
+    use unstable::finally::Finally;
+
+    // Our status variables will be filled in from the scheduler context
+    let mut failed = false;
+    let failed_ptr: *mut bool = &mut failed;
+
+    // Switch to the scheduler
+    let f = Cell(Cell(f));
+    let mut sched = local_sched::take();
+    do sched.deschedule_running_task_and_then() |old_task| {
+        let old_task = Cell(old_task);
+        let f = f.take();
+        let mut sched = local_sched::take();
+        let new_task = ~do Task::new(&mut sched.stack_pool) {
+            do (|| {
+                (f.take())()
+            }).finally {
+                // Check for failure then resume the parent task
+                unsafe { *failed_ptr = task::failing(); }
+                let sched = local_sched::take();
+                do sched.switch_running_tasks_and_then(old_task.take()) |new_task| {
+                    let new_task = Cell(new_task);
+                    do local_sched::borrow |sched| {
+                        sched.task_queue.push_front(new_task.take());
+                    }
+                }
+            }
+        };
+
+        sched.resume_task_immediately(new_task);
+    }
+
+    if !failed { Ok(()) } else { Err(()) }
+}
+
 /// Get a port number, starting at 9600, for use in tests
 pub fn next_test_port() -> u16 {
     unsafe {
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index 04f96f5eb22..c50bc03517f 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -134,12 +134,27 @@ pub fn log_str<T>(t: &T) -> ~str {
 
 /** Initiate task failure */
 pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! {
-    do str::as_buf(msg) |msg_buf, _msg_len| {
-        do str::as_buf(file) |file_buf, _file_len| {
+
+    use rt::{context, OldTaskContext};
+    use rt::local_services::unsafe_borrow_local_services;
+
+    match context() {
+        OldTaskContext => {
+            do str::as_buf(msg) |msg_buf, _msg_len| {
+                do str::as_buf(file) |file_buf, _file_len| {
+                    unsafe {
+                        let msg_buf = cast::transmute(msg_buf);
+                        let file_buf = cast::transmute(file_buf);
+                        begin_unwind_(msg_buf, file_buf, line as libc::size_t)
+                    }
+                }
+            }
+        }
+        _ => {
+            gc::cleanup_stack_for_failure();
             unsafe {
-                let msg_buf = cast::transmute(msg_buf);
-                let file_buf = cast::transmute(file_buf);
-                begin_unwind_(msg_buf, file_buf, line as libc::size_t)
+                let local_services = unsafe_borrow_local_services();
+                local_services.unwinder.begin_unwind();
             }
         }
     }
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index a243bfba85c..e1f4805a692 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -558,8 +558,22 @@ pub fn yield() {
 pub fn failing() -> bool {
     //! True if the running task has failed
 
-    unsafe {
-        rt::rust_task_is_unwinding(rt::rust_get_task())
+    use rt::{context, OldTaskContext};
+    use rt::local_services::borrow_local_services;
+
+    match context() {
+        OldTaskContext => {
+            unsafe {
+                rt::rust_task_is_unwinding(rt::rust_get_task())
+            }
+        }
+        _ => {
+            let mut unwinding = false;
+            do borrow_local_services |local| {
+                unwinding = local.unwinder.unwinding;
+            }
+            return unwinding;
+        }
     }
 }
 
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index b8749b8f73d..b37644460aa 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -886,6 +886,28 @@ rust_boxed_region_free(boxed_region *region, rust_opaque_box *box) {
     region->free(box);
 }
 
+typedef void *(rust_try_fn)(void*, void*);
+
+extern "C" CDECL uintptr_t
+rust_try(rust_try_fn f, void *fptr, void *env) {
+    try {
+        f(fptr, env);
+    } catch (uintptr_t token) {
+        assert(token != 0);
+        return token;
+    }
+    return 0;
+}
+
+extern "C" CDECL void
+rust_begin_unwind(uintptr_t token) {
+#ifndef __WIN32__
+    throw token;
+#else
+    abort("failing on win32");
+#endif
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 9f39e1433fc..34236c36c14 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -272,7 +272,13 @@ upcall_rust_personality(int version,
     s_rust_personality_args args = {(_Unwind_Reason_Code)0,
                                     version, actions, exception_class,
                                     ue_header, context};
-    rust_task *task = rust_get_current_task();
+    rust_task *task = rust_try_get_current_task();
+
+    if (task == NULL) {
+        // Assuming we're running with the new scheduler
+        upcall_s_rust_personality(&args);
+        return args.retval;
+    }
 
     // The personality function is run on the stack of the
     // last function that threw or landed, which is going
@@ -309,8 +315,12 @@ upcall_del_stack() {
 // needs to acquire the value of the stack pointer
 extern "C" CDECL void
 upcall_reset_stack_limit() {
-    rust_task *task = rust_get_current_task();
-    task->reset_stack_limit();
+    rust_task *task = rust_try_get_current_task();
+    if (task != NULL) {
+        task->reset_stack_limit();
+    } else {
+        // We must be in a newsched task
+    }
 }
 
 //
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 9aa80156783..5e9a4b343ee 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -228,3 +228,5 @@ rust_new_boxed_region
 rust_delete_boxed_region
 rust_boxed_region_malloc
 rust_boxed_region_free
+rust_try
+rust_begin_unwind
\ No newline at end of file

From a292d5175091f61e92d0f393275e32ad2b05d584 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 19:20:12 -0700
Subject: [PATCH 25/28] mk: Pass CFLAGS to the uv build

---
 mk/rt.mk | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/mk/rt.mk b/mk/rt.mk
index 015992abf78..538d1e2534f 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -169,7 +169,7 @@ $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS)
 else ifeq ($(OSTYPE_$(1)), linux-androideabi)
 $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS)
 	$$(Q)$$(MAKE) -C $$(S)src/libuv/ \
-		CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \
+		CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES) $$(CFLAGS)" \
 		LDFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1)))" \
 		CC="$$(CC_$(1))" \
 		CXX="$$(CXX_$(1))" \
@@ -181,7 +181,7 @@ $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS)
 else
 $$(LIBUV_LIB_$(1)): $$(LIBUV_DEPS)
 	$$(Q)$$(MAKE) -C $$(S)src/libuv/ \
-		CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES)" \
+		CFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1))) $$(SNAP_DEFINES) $$(CFLAGS)" \
 		LDFLAGS="$$(LIBUV_FLAGS_$$(HOST_$(1)))" \
 		CC="$$(CC_$(1))" \
 		CXX="$$(CXX_$(1))" \

From 8a2f9cae213d38b31e9372b9d8a7fee1263f1363 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 19:20:31 -0700
Subject: [PATCH 26/28] core::rt: Fix a use after free in uv 'write'

---
 src/libcore/rt/uv/mod.rs |  6 ++++--
 src/libcore/rt/uv/net.rs | 16 ++++++++++------
 src/libcore/rt/uvio.rs   |  5 ++++-
 src/libcore/rt/uvll.rs   |  6 +++---
 4 files changed, 21 insertions(+), 12 deletions(-)

diff --git a/src/libcore/rt/uv/mod.rs b/src/libcore/rt/uv/mod.rs
index 32757d6376e..4c4b2e03d3d 100644
--- a/src/libcore/rt/uv/mod.rs
+++ b/src/libcore/rt/uv/mod.rs
@@ -301,7 +301,8 @@ struct WatcherData {
     write_cb: Option<ConnectionCallback>,
     connect_cb: Option<ConnectionCallback>,
     close_cb: Option<NullCallback>,
-    alloc_cb: Option<AllocCallback>
+    alloc_cb: Option<AllocCallback>,
+    buf: Option<Buf>
 }
 
 pub fn install_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
@@ -311,7 +312,8 @@ pub fn install_watcher_data<H, W: Watcher + NativeHandle<*H>>(watcher: &mut W) {
             write_cb: None,
             connect_cb: None,
             close_cb: None,
-            alloc_cb: None
+            alloc_cb: None,
+            buf: None
         };
         let data = transmute::<~WatcherData, *c_void>(data);
         uvll::set_data_for_uv_handle(watcher.native_handle(), data);
diff --git a/src/libcore/rt/uv/net.rs b/src/libcore/rt/uv/net.rs
index 860c988b9c9..04b9008b067 100644
--- a/src/libcore/rt/uv/net.rs
+++ b/src/libcore/rt/uv/net.rs
@@ -107,21 +107,25 @@ pub impl StreamWatcher {
 
         let req = WriteRequest::new();
         let buf = vec_to_uv_buf(msg);
-        // XXX: Allocation
-        let bufs = ~[buf];
+        assert!(data.buf.is_none());
+        data.buf = Some(buf);
+        let bufs = [buf];
         unsafe {
             assert!(0 == uvll::write(req.native_handle(),
                                           self.native_handle(),
-                                          &bufs, write_cb));
+                                          bufs, write_cb));
         }
-        // XXX: Freeing immediately after write. Is this ok?
-        let _v = vec_from_uv_buf(buf);
 
         extern fn write_cb(req: *uvll::uv_write_t, status: c_int) {
             let write_request: WriteRequest = NativeHandle::from_native_handle(req);
             let mut stream_watcher = write_request.stream();
             write_request.delete();
-            let cb = get_watcher_data(&mut stream_watcher).write_cb.swap_unwrap();
+            let cb = {
+                let data = get_watcher_data(&mut stream_watcher);
+                let _vec = vec_from_uv_buf(data.buf.swap_unwrap());
+                let cb = data.write_cb.swap_unwrap();
+                cb
+            };
             let status = status_to_maybe_uv_error(stream_watcher.native_handle(), status);
             cb(stream_watcher, status);
         }
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index 2e9d0afa52f..e7b2880b74b 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -445,7 +445,7 @@ fn test_read_read_read() {
                 let io = local_sched::unsafe_borrow_io();
                 let mut listener = io.bind(addr).unwrap();
                 let mut stream = listener.listen().unwrap();
-                let mut buf = [0, .. 2048];
+                let mut buf = [1, .. 2048];
                 let mut total_bytes_written = 0;
                 while total_bytes_written < MAX {
                     stream.write(buf);
@@ -465,6 +465,9 @@ fn test_read_read_read() {
                 let nread = stream.read(buf).unwrap();
                 rtdebug!("read %u bytes", nread as uint);
                 total_bytes_read += nread;
+                for uint::range(0, nread) |i| {
+                    assert!(buf[i] == 1);
+                }
             }
             rtdebug!("read %u bytes total", total_bytes_read as uint);
             stream.close();
diff --git a/src/libcore/rt/uvll.rs b/src/libcore/rt/uvll.rs
index 640a69743ba..0f75ebb6090 100644
--- a/src/libcore/rt/uvll.rs
+++ b/src/libcore/rt/uvll.rs
@@ -219,9 +219,9 @@ pub unsafe fn accept(server: *c_void, client: *c_void) -> c_int {
     return rust_uv_accept(server as *c_void, client as *c_void);
 }
 
-pub unsafe fn write<T>(req: *uv_write_t, stream: *T, buf_in: *~[uv_buf_t], cb: *u8) -> c_int {
-    let buf_ptr = vec::raw::to_ptr(*buf_in);
-    let buf_cnt = vec::len(*buf_in) as i32;
+pub unsafe fn write<T>(req: *uv_write_t, stream: *T, buf_in: &[uv_buf_t], cb: *u8) -> c_int {
+    let buf_ptr = vec::raw::to_ptr(buf_in);
+    let buf_cnt = vec::len(buf_in) as i32;
     return rust_uv_write(req as *c_void, stream as *c_void, buf_ptr, buf_cnt, cb);
 }
 pub unsafe fn read_start(stream: *uv_stream_t, on_alloc: *u8, on_read: *u8) -> c_int {

From f4af40a1db4862cfe1f17311e3e39cfff3324d82 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Mon, 22 Apr 2013 21:27:24 -0700
Subject: [PATCH 27/28] mk: core has another level of directories with
 rt/io/net

---
 Makefile.in | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Makefile.in b/Makefile.in
index dd2e6a95861..33077872c16 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -238,7 +238,7 @@ $(foreach target,$(CFG_TARGET_TRIPLES),\
 
 CORELIB_CRATE := $(S)src/libcore/core.rc
 CORELIB_INPUTS := $(wildcard $(addprefix $(S)src/libcore/,        \
-                                           core.rc *.rs */*.rs */*/*rs))
+                                           core.rc *.rs */*.rs */*/*rs */*/*/*rs))
 
 ######################################################################
 # Standard library variables

From 2a819ae465c5f375df00ead0b3f4c9009da23f25 Mon Sep 17 00:00:00 2001
From: Brian Anderson <banderson@mozilla.com>
Date: Tue, 23 Apr 2013 15:11:28 -0700
Subject: [PATCH 28/28] core::rt: Tasks to not require an unwinder

A task without an unwinder will abort the process on failure.
I'm using this in the runtime tests to guarantee that a call to
`assert!` actually triggers some kind of failure (an abort)
instead of silently doing nothing. This is essentially in lieu
of a working linked failure implementation.
---
 src/libcore/core.rc              |  3 +++
 src/libcore/macros.rs            | 39 +++++++++++++++++++++++++++++
 src/libcore/rt/local_services.rs | 30 +++++++++++++++++++----
 src/libcore/rt/mod.rs            | 20 ---------------
 src/libcore/rt/sched/mod.rs      | 10 ++++++--
 src/libcore/rt/test.rs           | 42 +++++++++++++++++++++++---------
 src/libcore/rt/uvio.rs           | 12 ++++-----
 src/libcore/sys.rs               |  6 ++++-
 src/libcore/task/mod.rs          | 11 ++++++++-
 9 files changed, 126 insertions(+), 47 deletions(-)
 create mode 100644 src/libcore/macros.rs

diff --git a/src/libcore/core.rc b/src/libcore/core.rc
index e7a5cfbaf4b..a3b2cb4aaf9 100644
--- a/src/libcore/core.rc
+++ b/src/libcore/core.rc
@@ -114,6 +114,9 @@ pub mod linkhack {
     }
 }
 
+// Internal macros
+mod macros;
+
 /* The Prelude. */
 
 pub mod prelude;
diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs
new file mode 100644
index 00000000000..e1276a75e05
--- /dev/null
+++ b/src/libcore/macros.rs
@@ -0,0 +1,39 @@
+// 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.
+
+#[macro_escape];
+
+// Some basic logging
+macro_rules! rtdebug (
+    ($( $arg:expr),+) => ( {
+        dumb_println(fmt!( $($arg),+ ));
+
+        fn dumb_println(s: &str) {
+            use io::WriterUtil;
+            let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
+            dbg.write_str(s);
+            dbg.write_str("\n");
+        }
+
+    } )
+)
+
+// An alternate version with no output, for turning off logging
+macro_rules! rtdebug_ (
+    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
+)
+
+macro_rules! abort(
+    ($( $msg:expr),+) => ( {
+        rtdebug!($($msg),+);
+
+        unsafe { ::libc::abort(); }
+    } )
+)
diff --git a/src/libcore/rt/local_services.rs b/src/libcore/rt/local_services.rs
index fc75a256428..d09d082c858 100644
--- a/src/libcore/rt/local_services.rs
+++ b/src/libcore/rt/local_services.rs
@@ -29,7 +29,7 @@ pub struct LocalServices {
     gc: GarbageCollector,
     storage: LocalStorage,
     logger: Logger,
-    unwinder: Unwinder,
+    unwinder: Option<Unwinder>,
     destroyed: bool
 }
 
@@ -48,7 +48,18 @@ impl LocalServices {
             gc: GarbageCollector,
             storage: LocalStorage(ptr::null(), None),
             logger: Logger,
-            unwinder: Unwinder { unwinding: false },
+            unwinder: Some(Unwinder { unwinding: false }),
+            destroyed: false
+        }
+    }
+
+    pub fn without_unwinding() -> LocalServices {
+        LocalServices {
+            heap: LocalHeap::new(),
+            gc: GarbageCollector,
+            storage: LocalStorage(ptr::null(), None),
+            logger: Logger,
+            unwinder: None,
             destroyed: false
         }
     }
@@ -60,7 +71,16 @@ impl LocalServices {
             assert!(ptr::ref_eq(sched, self));
         }
 
-        self.unwinder.try(f);
+        match self.unwinder {
+            Some(ref mut unwinder) => {
+                // If there's an unwinder then set up the catch block
+                unwinder.try(f);
+            }
+            None => {
+                // Otherwise, just run the body
+                f()
+            }
+        }
         self.destroy();
     }
 
@@ -189,9 +209,9 @@ mod test {
     #[test]
     fn unwind() {
         do run_in_newsched_task() {
-            let result = spawn_try(||());
+            let result = spawntask_try(||());
             assert!(result.is_ok());
-            let result = spawn_try(|| fail!());
+            let result = spawntask_try(|| fail!());
             assert!(result.is_err());
         }
     }
diff --git a/src/libcore/rt/mod.rs b/src/libcore/rt/mod.rs
index 4a767d61f74..ab89a4c26a5 100644
--- a/src/libcore/rt/mod.rs
+++ b/src/libcore/rt/mod.rs
@@ -12,26 +12,6 @@
 
 use libc::c_char;
 
-// Some basic logging
-macro_rules! rtdebug_ (
-    ($( $arg:expr),+) => ( {
-        dumb_println(fmt!( $($arg),+ ));
-
-        fn dumb_println(s: &str) {
-            use io::WriterUtil;
-            let dbg = ::libc::STDERR_FILENO as ::io::fd_t;
-            dbg.write_str(s);
-            dbg.write_str("\n");
-        }
-
-    } )
-)
-
-// An alternate version with no output, for turning off logging
-macro_rules! rtdebug (
-    ($( $arg:expr),+) => ( $(let _ = $arg)*; )
-)
-
 #[path = "sched/mod.rs"]
 mod sched;
 mod rtio;
diff --git a/src/libcore/rt/sched/mod.rs b/src/libcore/rt/sched/mod.rs
index 65456c30fee..f7b9bd82668 100644
--- a/src/libcore/rt/sched/mod.rs
+++ b/src/libcore/rt/sched/mod.rs
@@ -149,7 +149,7 @@ pub impl Scheduler {
             }
         }
 
-        // Control never reaches here
+        abort!("control reached end of task");
     }
 
     fn schedule_new_task(~self, task: ~Task) {
@@ -333,6 +333,12 @@ pub struct Task {
 
 pub impl Task {
     fn new(stack_pool: &mut StackPool, start: ~fn()) -> Task {
+        Task::with_local(stack_pool, LocalServices::new(), start)
+    }
+
+    fn with_local(stack_pool: &mut StackPool,
+                  local_services: LocalServices,
+                  start: ~fn()) -> Task {
         let start = Task::build_start_wrapper(start);
         let mut stack = stack_pool.take_segment(TASK_MIN_STACK_SIZE);
         // NB: Context holds a pointer to that ~fn
@@ -340,7 +346,7 @@ pub impl Task {
         return Task {
             current_stack_segment: stack,
             saved_context: initial_context,
-            local_services: LocalServices::new()
+            local_services: local_services
         };
     }
 
diff --git a/src/libcore/rt/test.rs b/src/libcore/rt/test.rs
index f3d73c91bd6..f7ba881f84e 100644
--- a/src/libcore/rt/test.rs
+++ b/src/libcore/rt/test.rs
@@ -8,38 +8,56 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use cell::Cell;
 use result::{Result, Ok, Err};
 use super::io::net::ip::{IpAddr, Ipv4};
+use rt::local_services::LocalServices;
 
 /// Creates a new scheduler in a new thread and runs a task in it,
-/// then waits for the scheduler to exit.
+/// then waits for the scheduler to exit. Failure of the task
+/// will abort the process.
 pub fn run_in_newsched_task(f: ~fn()) {
-    use cell::Cell;
     use unstable::run_in_bare_thread;
     use super::sched::Task;
     use super::uvio::UvEventLoop;
 
-    let f = Cell(Cell(f));
+    let f = Cell(f);
 
     do run_in_bare_thread {
         let mut sched = ~UvEventLoop::new_scheduler();
-        let f = f.take();
-        let task = ~do Task::new(&mut sched.stack_pool) {
-            (f.take())();
-        };
+        let task = ~Task::with_local(&mut sched.stack_pool,
+                                     LocalServices::without_unwinding(),
+                                     f.take());
         sched.task_queue.push_back(task);
         sched.run();
     }
 }
 
-/// Create a new task and run it right now
-pub fn spawn_immediately(f: ~fn()) {
-    use cell::Cell;
+/// Test tasks will abort on failure instead of unwinding
+pub fn spawntask(f: ~fn()) {
     use super::*;
     use super::sched::*;
 
     let mut sched = local_sched::take();
-    let task = ~Task::new(&mut sched.stack_pool, f);
+    let task = ~Task::with_local(&mut sched.stack_pool,
+                                 LocalServices::without_unwinding(),
+                                 f);
+    do sched.switch_running_tasks_and_then(task) |task| {
+        let task = Cell(task);
+        let sched = local_sched::take();
+        sched.schedule_new_task(task.take());
+    }
+}
+
+/// Create a new task and run it right now. Aborts on failure
+pub fn spawntask_immediately(f: ~fn()) {
+    use super::*;
+    use super::sched::*;
+
+    let mut sched = local_sched::take();
+    let task = ~Task::with_local(&mut sched.stack_pool,
+                                 LocalServices::without_unwinding(),
+                                 f);
     do sched.switch_running_tasks_and_then(task) |task| {
         let task = Cell(task);
         do local_sched::borrow |sched| {
@@ -49,7 +67,7 @@ pub fn spawn_immediately(f: ~fn()) {
 }
 
 /// Spawn a task and wait for it to finish, returning whether it completed successfully or failed
-pub fn spawn_try(f: ~fn()) -> Result<(), ()> {
+pub fn spawntask_try(f: ~fn()) -> Result<(), ()> {
     use cell::Cell;
     use super::sched::*;
     use task;
diff --git a/src/libcore/rt/uvio.rs b/src/libcore/rt/uvio.rs
index e7b2880b74b..4cceb048cbc 100644
--- a/src/libcore/rt/uvio.rs
+++ b/src/libcore/rt/uvio.rs
@@ -350,7 +350,7 @@ fn test_simple_tcp_server_and_client() {
         let addr = next_test_ip4();
 
         // Start the server first so it's listening when we connect
-        do spawn_immediately {
+        do spawntask_immediately {
             unsafe {
                 let io = local_sched::unsafe_borrow_io();
                 let mut listener = io.bind(addr).unwrap();
@@ -367,7 +367,7 @@ fn test_simple_tcp_server_and_client() {
             }
         }
 
-        do spawn_immediately {
+        do spawntask_immediately {
             unsafe {
                 let io = local_sched::unsafe_borrow_io();
                 let mut stream = io.connect(addr).unwrap();
@@ -383,7 +383,7 @@ fn test_read_and_block() {
     do run_in_newsched_task {
         let addr = next_test_ip4();
 
-        do spawn_immediately {
+        do spawntask_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut listener = io.bind(addr).unwrap();
             let mut stream = listener.listen().unwrap();
@@ -421,7 +421,7 @@ fn test_read_and_block() {
             listener.close();
         }
 
-        do spawn_immediately {
+        do spawntask_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut stream = io.connect(addr).unwrap();
             stream.write([0, 1, 2, 3, 4, 5, 6, 7]);
@@ -440,7 +440,7 @@ fn test_read_read_read() {
         let addr = next_test_ip4();
         static MAX: uint = 500000;
         
-        do spawn_immediately {
+        do spawntask_immediately {
             unsafe {
                 let io = local_sched::unsafe_borrow_io();
                 let mut listener = io.bind(addr).unwrap();
@@ -456,7 +456,7 @@ fn test_read_read_read() {
             }
         }
 
-        do spawn_immediately {
+        do spawntask_immediately {
             let io = unsafe { local_sched::unsafe_borrow_io() };
             let mut stream = io.connect(addr).unwrap();
             let mut buf = [0, .. 2048];
diff --git a/src/libcore/sys.rs b/src/libcore/sys.rs
index c50bc03517f..2c3670ad498 100644
--- a/src/libcore/sys.rs
+++ b/src/libcore/sys.rs
@@ -10,6 +10,7 @@
 
 //! Misc low level stuff
 
+use option::{Some, None};
 use cast;
 use cmp::{Eq, Ord};
 use gc;
@@ -154,7 +155,10 @@ pub fn begin_unwind(msg: ~str, file: ~str, line: uint) -> ! {
             gc::cleanup_stack_for_failure();
             unsafe {
                 let local_services = unsafe_borrow_local_services();
-                local_services.unwinder.begin_unwind();
+                match local_services.unwinder {
+                    Some(ref mut unwinder) => unwinder.begin_unwind(),
+                    None => abort!("failure without unwinder. aborting process")
+                }
             }
         }
     }
diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs
index e1f4805a692..d31a511eca8 100644
--- a/src/libcore/task/mod.rs
+++ b/src/libcore/task/mod.rs
@@ -570,7 +570,16 @@ pub fn failing() -> bool {
         _ => {
             let mut unwinding = false;
             do borrow_local_services |local| {
-                unwinding = local.unwinder.unwinding;
+                unwinding = match local.unwinder {
+                    Some(unwinder) => {
+                        unwinder.unwinding
+                    }
+                    None => {
+                        // Because there is no unwinder we can't be unwinding.
+                        // (The process will abort on failure)
+                        false
+                    }
+                }
             }
             return unwinding;
         }