From 3a527f2b3311d5b1c6dd7c72db71c45596e6db49 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Tue, 30 Sep 2014 17:03:56 -0700
Subject: [PATCH 01/11] Runtime removal: add private sys, sys_common modules

These modules will house the code that used to be part of the runtime system
in libnative. The `sys_common` module contains a few low-level but
cross-platform details. The `sys` module is set up using `#[cfg()]` to
include either a unix or windows implementation of a common API
surface. This API surface is *not* exported directly in `libstd`, but is
instead used to bulid `std::os` and `std::io`.

Ultimately, the low-level details in `sys` will be exposed in a
controlled way through a separate platform-specific surface, but that
setup is not part of this patch.
---
 src/libnative/io/mod.rs                       |   3 -
 src/libnative/io/util.rs                      | 209 ------------------
 src/libstd/io/mod.rs                          |  92 +-------
 src/libstd/lib.rs                             |   7 +
 src/libstd/os.rs                              | 155 +------------
 src/libstd/sys/common/mod.rs                  |  91 ++++++++
 .../io/c_unix.rs => libstd/sys/unix/c.rs}     |   3 +-
 src/libstd/sys/unix/mod.rs                    |  92 ++++++++
 src/libstd/sys/unix/os.rs                     | 101 +++++++++
 .../c_windows.rs => libstd/sys/windows/c.rs}  |  14 +-
 src/libstd/sys/windows/mod.rs                 | 178 +++++++++++++++
 src/libstd/sys/windows/os.rs                  | 103 +++++++++
 12 files changed, 592 insertions(+), 456 deletions(-)
 delete mode 100644 src/libnative/io/util.rs
 create mode 100644 src/libstd/sys/common/mod.rs
 rename src/{libnative/io/c_unix.rs => libstd/sys/unix/c.rs} (99%)
 create mode 100644 src/libstd/sys/unix/mod.rs
 create mode 100644 src/libstd/sys/unix/os.rs
 rename src/{libnative/io/c_windows.rs => libstd/sys/windows/c.rs} (95%)
 create mode 100644 src/libstd/sys/windows/mod.rs
 create mode 100644 src/libstd/sys/windows/os.rs

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 954f7bbc59adc..90f8f6c214e78 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -73,9 +73,6 @@ pub mod pipe;
 #[path = "tty_windows.rs"]
 mod tty;
 
-#[cfg(unix)]    #[path = "c_unix.rs"]  mod c;
-#[cfg(windows)] #[path = "c_windows.rs"] mod c;
-
 fn unimpl() -> IoError {
     #[cfg(unix)] use libc::ENOSYS as ERROR;
     #[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;
diff --git a/src/libnative/io/util.rs b/src/libnative/io/util.rs
deleted file mode 100644
index 5f69ec00cddd4..0000000000000
--- a/src/libnative/io/util.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2014 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 libc;
-use std::cmp;
-use std::mem;
-use std::os;
-use std::ptr;
-use std::rt::rtio::{IoResult, IoError};
-
-use super::c;
-use super::net;
-use super::{retry, last_error};
-
-#[deriving(Show)]
-pub enum SocketStatus {
-    Readable,
-    Writable,
-}
-
-pub fn timeout(desc: &'static str) -> IoError {
-    #[cfg(unix)] use libc::ETIMEDOUT as ERROR;
-    #[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
-    IoError {
-        code: ERROR as uint,
-        extra: 0,
-        detail: Some(desc.to_string()),
-    }
-}
-
-pub fn short_write(n: uint, desc: &'static str) -> IoError {
-    #[cfg(unix)] use libc::EAGAIN as ERROR;
-    #[cfg(windows)] use libc::ERROR_OPERATION_ABORTED as ERROR;
-    IoError {
-        code: ERROR as uint,
-        extra: n,
-        detail: Some(desc.to_string()),
-    }
-}
-
-pub fn eof() -> IoError {
-    IoError {
-        code: libc::EOF as uint,
-        extra: 0,
-        detail: None,
-    }
-}
-
-#[cfg(windows)]
-pub fn ms_to_timeval(ms: u64) -> libc::timeval {
-    libc::timeval {
-        tv_sec: (ms / 1000) as libc::c_long,
-        tv_usec: ((ms % 1000) * 1000) as libc::c_long,
-    }
-}
-#[cfg(not(windows))]
-pub fn ms_to_timeval(ms: u64) -> libc::timeval {
-    libc::timeval {
-        tv_sec: (ms / 1000) as libc::time_t,
-        tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
-    }
-}
-
-#[cfg(unix)]
-pub fn wouldblock() -> bool {
-    let err = os::errno();
-    err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
-}
-
-#[cfg(windows)]
-pub fn wouldblock() -> bool {
-    let err = os::errno();
-    err == libc::WSAEWOULDBLOCK as uint
-}
-
-#[cfg(unix)]
-pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
-    let set = nb as libc::c_int;
-    super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
-}
-
-#[cfg(windows)]
-pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
-    let mut set = nb as libc::c_ulong;
-    if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
-        Err(last_error())
-    } else {
-        Ok(())
-    }
-}
-
-// See http://developerweb.net/viewtopic.php?id=3196 for where this is
-// derived from.
-pub fn connect_timeout(fd: net::sock_t,
-                       addrp: *const libc::sockaddr,
-                       len: libc::socklen_t,
-                       timeout_ms: u64) -> IoResult<()> {
-    use std::os;
-    #[cfg(unix)]    use libc::EINPROGRESS as INPROGRESS;
-    #[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
-    #[cfg(unix)]    use libc::EWOULDBLOCK as WOULDBLOCK;
-    #[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
-
-    // Make sure the call to connect() doesn't block
-    try!(set_nonblocking(fd, true));
-
-    let ret = match unsafe { libc::connect(fd, addrp, len) } {
-        // If the connection is in progress, then we need to wait for it to
-        // finish (with a timeout). The current strategy for doing this is
-        // to use select() with a timeout.
-        -1 if os::errno() as int == INPROGRESS as int ||
-              os::errno() as int == WOULDBLOCK as int => {
-            let mut set: c::fd_set = unsafe { mem::zeroed() };
-            c::fd_set(&mut set, fd);
-            match await(fd, &mut set, timeout_ms) {
-                0 => Err(timeout("connection timed out")),
-                -1 => Err(last_error()),
-                _ => {
-                    let err: libc::c_int = try!(
-                        net::getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
-                    if err == 0 {
-                        Ok(())
-                    } else {
-                        Err(IoError {
-                            code: err as uint,
-                            extra: 0,
-                            detail: Some(os::error_string(err as uint)),
-                        })
-                    }
-                }
-            }
-        }
-
-        -1 => Err(last_error()),
-        _ => Ok(()),
-    };
-
-    // be sure to turn blocking I/O back on
-    try!(set_nonblocking(fd, false));
-    return ret;
-
-    #[cfg(unix)]
-    fn await(fd: net::sock_t, set: &mut c::fd_set,
-             timeout: u64) -> libc::c_int {
-        let start = ::io::timer::now();
-        retry(|| unsafe {
-            // Recalculate the timeout each iteration (it is generally
-            // undefined what the value of the 'tv' is after select
-            // returns EINTR).
-            let mut tv = ms_to_timeval(timeout - (::io::timer::now() - start));
-            c::select(fd + 1, ptr::null_mut(), set as *mut _,
-                      ptr::null_mut(), &mut tv)
-        })
-    }
-    #[cfg(windows)]
-    fn await(_fd: net::sock_t, set: &mut c::fd_set,
-             timeout: u64) -> libc::c_int {
-        let mut tv = ms_to_timeval(timeout);
-        unsafe { c::select(1, ptr::null_mut(), set, ptr::null_mut(), &mut tv) }
-    }
-}
-
-pub fn await(fds: &[net::sock_t], deadline: Option<u64>,
-             status: SocketStatus) -> IoResult<()> {
-    let mut set: c::fd_set = unsafe { mem::zeroed() };
-    let mut max = 0;
-    for &fd in fds.iter() {
-        c::fd_set(&mut set, fd);
-        max = cmp::max(max, fd + 1);
-    }
-    if cfg!(windows) {
-        max = fds.len() as net::sock_t;
-    }
-
-    let (read, write) = match status {
-        Readable => (&mut set as *mut _, ptr::null_mut()),
-        Writable => (ptr::null_mut(), &mut set as *mut _),
-    };
-    let mut tv: libc::timeval = unsafe { mem::zeroed() };
-
-    match retry(|| {
-        let now = ::io::timer::now();
-        let tvp = match deadline {
-            None => ptr::null_mut(),
-            Some(deadline) => {
-                // If we're past the deadline, then pass a 0 timeout to
-                // select() so we can poll the status
-                let ms = if deadline < now {0} else {deadline - now};
-                tv = ms_to_timeval(ms);
-                &mut tv as *mut _
-            }
-        };
-        let r = unsafe {
-            c::select(max as libc::c_int, read, write, ptr::null_mut(), tvp)
-        };
-        r
-    }) {
-        -1 => Err(last_error()),
-        0 => Err(timeout("timed out")),
-        _ => Ok(()),
-    }
-}
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index c404741b7c31a..78abbb9f80df7 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -236,8 +236,7 @@ use os;
 use boxed::Box;
 use result::{Ok, Err, Result};
 use rt::rtio;
-use slice::{AsSlice, SlicePrelude};
-use str::{Str, StrPrelude};
+use sys;
 use str;
 use string::String;
 use uint;
@@ -312,91 +311,10 @@ impl IoError {
     /// struct is filled with an allocated string describing the error
     /// in more detail, retrieved from the operating system.
     pub fn from_errno(errno: uint, detail: bool) -> IoError {
-
-        #[cfg(windows)]
-        fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
-            match errno {
-                libc::EOF => (EndOfFile, "end of file"),
-                libc::ERROR_NO_DATA => (BrokenPipe, "the pipe is being closed"),
-                libc::ERROR_FILE_NOT_FOUND => (FileNotFound, "file not found"),
-                libc::ERROR_INVALID_NAME => (InvalidInput, "invalid file name"),
-                libc::WSAECONNREFUSED => (ConnectionRefused, "connection refused"),
-                libc::WSAECONNRESET => (ConnectionReset, "connection reset"),
-                libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
-                    (PermissionDenied, "permission denied"),
-                libc::WSAEWOULDBLOCK => {
-                    (ResourceUnavailable, "resource temporarily unavailable")
-                }
-                libc::WSAENOTCONN => (NotConnected, "not connected"),
-                libc::WSAECONNABORTED => (ConnectionAborted, "connection aborted"),
-                libc::WSAEADDRNOTAVAIL => (ConnectionRefused, "address not available"),
-                libc::WSAEADDRINUSE => (ConnectionRefused, "address in use"),
-                libc::ERROR_BROKEN_PIPE => (EndOfFile, "the pipe has ended"),
-                libc::ERROR_OPERATION_ABORTED =>
-                    (TimedOut, "operation timed out"),
-                libc::WSAEINVAL => (InvalidInput, "invalid argument"),
-                libc::ERROR_CALL_NOT_IMPLEMENTED =>
-                    (IoUnavailable, "function not implemented"),
-                libc::ERROR_INVALID_HANDLE =>
-                    (MismatchedFileTypeForOperation,
-                     "invalid handle provided to function"),
-                libc::ERROR_NOTHING_TO_TERMINATE =>
-                    (InvalidInput, "no process to kill"),
-
-                // libuv maps this error code to EISDIR. we do too. if it is found
-                // to be incorrect, we can add in some more machinery to only
-                // return this message when ERROR_INVALID_FUNCTION after certain
-                // Windows calls.
-                libc::ERROR_INVALID_FUNCTION => (InvalidInput,
-                                                 "illegal operation on a directory"),
-
-                _ => (OtherIoError, "unknown error")
-            }
-        }
-
-        #[cfg(not(windows))]
-        fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
-            // FIXME: this should probably be a bit more descriptive...
-            match errno {
-                libc::EOF => (EndOfFile, "end of file"),
-                libc::ECONNREFUSED => (ConnectionRefused, "connection refused"),
-                libc::ECONNRESET => (ConnectionReset, "connection reset"),
-                libc::EPERM | libc::EACCES =>
-                    (PermissionDenied, "permission denied"),
-                libc::EPIPE => (BrokenPipe, "broken pipe"),
-                libc::ENOTCONN => (NotConnected, "not connected"),
-                libc::ECONNABORTED => (ConnectionAborted, "connection aborted"),
-                libc::EADDRNOTAVAIL => (ConnectionRefused, "address not available"),
-                libc::EADDRINUSE => (ConnectionRefused, "address in use"),
-                libc::ENOENT => (FileNotFound, "no such file or directory"),
-                libc::EISDIR => (InvalidInput, "illegal operation on a directory"),
-                libc::ENOSYS => (IoUnavailable, "function not implemented"),
-                libc::EINVAL => (InvalidInput, "invalid argument"),
-                libc::ENOTTY =>
-                    (MismatchedFileTypeForOperation,
-                     "file descriptor is not a TTY"),
-                libc::ETIMEDOUT => (TimedOut, "operation timed out"),
-                libc::ECANCELED => (TimedOut, "operation aborted"),
-
-                // These two constants can have the same value on some systems,
-                // but different values on others, so we can't use a match
-                // clause
-                x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
-                    (ResourceUnavailable, "resource temporarily unavailable"),
-
-                _ => (OtherIoError, "unknown error")
-            }
-        }
-
-        let (kind, desc) = get_err(errno as i32);
-        IoError {
-            kind: kind,
-            desc: desc,
-            detail: if detail && kind == OtherIoError {
-                Some(os::error_string(errno).as_slice().chars().map(|c| c.to_lowercase()).collect())
-            } else {
-                None
-            },
+        let mut err = sys::decode_error(errno as i32);
+        if detail && err.kind == OtherIoError {
+            err.detail = Some(os::error_string(errno).as_slice().chars()
+                                 .map(|c| c.to_lowercase()).collect())
         }
     }
 
diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs
index f10a1d5e5edc7..7eac455f97f25 100644
--- a/src/libstd/lib.rs
+++ b/src/libstd/lib.rs
@@ -242,6 +242,13 @@ pub mod io;
 pub mod path;
 pub mod fmt;
 
+#[cfg(unix)]
+#[path = "sys/unix/mod.rs"] mod sys;
+#[cfg(windows)]
+#[path = "sys/windows/mod.rs"] mod sys;
+
+#[path = "sys/common/mod.rs"] mod sys_common;
+
 // FIXME #7809: This shouldn't be pub, and it should be reexported under 'unstable'
 // but name resolution doesn't work without it being pub.
 pub mod rt;
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index 0042a3ae20592..175e23bf8192a 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -34,7 +34,7 @@
 use clone::Clone;
 use error::{FromError, Error};
 use fmt;
-use io::{IoResult, IoError};
+use io::IoResult;
 use iter::Iterator;
 use libc::{c_void, c_int};
 use libc;
@@ -43,6 +43,7 @@ use ops::Drop;
 use option::{Some, None, Option};
 use os;
 use path::{Path, GenericPath, BytesContainer};
+use sys::os as os_imp;
 use ptr::RawPtr;
 use ptr;
 use result::{Err, Ok, Result};
@@ -905,59 +906,9 @@ pub fn change_dir(p: &Path) -> bool {
     }
 }
 
-#[cfg(unix)]
-/// Returns the platform-specific value of errno
-pub fn errno() -> int {
-    #[cfg(any(target_os = "macos",
-              target_os = "ios",
-              target_os = "freebsd"))]
-    fn errno_location() -> *const c_int {
-        extern {
-            fn __error() -> *const c_int;
-        }
-        unsafe {
-            __error()
-        }
-    }
-
-    #[cfg(target_os = "dragonfly")]
-    fn errno_location() -> *const c_int {
-        extern {
-            fn __dfly_error() -> *const c_int;
-        }
-        unsafe {
-            __dfly_error()
-        }
-    }
-
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn errno_location() -> *const c_int {
-        extern {
-            fn __errno_location() -> *const c_int;
-        }
-        unsafe {
-            __errno_location()
-        }
-    }
-
-    unsafe {
-        (*errno_location()) as int
-    }
-}
-
-#[cfg(windows)]
 /// Returns the platform-specific value of errno
 pub fn errno() -> uint {
-    use libc::types::os::arch::extra::DWORD;
-
-    #[link_name = "kernel32"]
-    extern "system" {
-        fn GetLastError() -> DWORD;
-    }
-
-    unsafe {
-        GetLastError() as uint
-    }
+    os_imp::errno() as uint
 }
 
 /// Return the string corresponding to an `errno()` value of `errnum`.
@@ -969,105 +920,7 @@ pub fn errno() -> uint {
 /// println!("{}", os::error_string(os::errno() as uint));
 /// ```
 pub fn error_string(errnum: uint) -> String {
-    return strerror(errnum);
-
-    #[cfg(unix)]
-    fn strerror(errnum: uint) -> String {
-        #[cfg(any(target_os = "macos",
-                  target_os = "ios",
-                  target_os = "android",
-                  target_os = "freebsd",
-                  target_os = "dragonfly"))]
-        fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
-                      -> c_int {
-            extern {
-                fn strerror_r(errnum: c_int, buf: *mut c_char,
-                              buflen: libc::size_t) -> c_int;
-            }
-            unsafe {
-                strerror_r(errnum, buf, buflen)
-            }
-        }
-
-        // GNU libc provides a non-compliant version of strerror_r by default
-        // and requires macros to instead use the POSIX compliant variant.
-        // So we just use __xpg_strerror_r which is always POSIX compliant
-        #[cfg(target_os = "linux")]
-        fn strerror_r(errnum: c_int, buf: *mut c_char,
-                      buflen: libc::size_t) -> c_int {
-            extern {
-                fn __xpg_strerror_r(errnum: c_int,
-                                    buf: *mut c_char,
-                                    buflen: libc::size_t)
-                                    -> c_int;
-            }
-            unsafe {
-                __xpg_strerror_r(errnum, buf, buflen)
-            }
-        }
-
-        let mut buf = [0 as c_char, ..TMPBUF_SZ];
-
-        let p = buf.as_mut_ptr();
-        unsafe {
-            if strerror_r(errnum as c_int, p, buf.len() as libc::size_t) < 0 {
-                panic!("strerror_r failure");
-            }
-
-            ::string::raw::from_buf(p as *const u8)
-        }
-    }
-
-    #[cfg(windows)]
-    fn strerror(errnum: uint) -> String {
-        use libc::types::os::arch::extra::DWORD;
-        use libc::types::os::arch::extra::LPWSTR;
-        use libc::types::os::arch::extra::LPVOID;
-        use libc::types::os::arch::extra::WCHAR;
-
-        #[link_name = "kernel32"]
-        extern "system" {
-            fn FormatMessageW(flags: DWORD,
-                              lpSrc: LPVOID,
-                              msgId: DWORD,
-                              langId: DWORD,
-                              buf: LPWSTR,
-                              nsize: DWORD,
-                              args: *const c_void)
-                              -> DWORD;
-        }
-
-        static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
-        static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
-
-        // This value is calculated from the macro
-        // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
-        let langId = 0x0800 as DWORD;
-
-        let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
-
-        unsafe {
-            let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
-                                     FORMAT_MESSAGE_IGNORE_INSERTS,
-                                     ptr::null_mut(),
-                                     errnum as DWORD,
-                                     langId,
-                                     buf.as_mut_ptr(),
-                                     buf.len() as DWORD,
-                                     ptr::null());
-            if res == 0 {
-                // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
-                let fm_err = errno();
-                return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
-            }
-
-            let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
-            match msg {
-                Some(msg) => format!("OS Error {}: {}", errnum, msg),
-                None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
-            }
-        }
-    }
+    return os_imp::error_string(errnum as i32);
 }
 
 /// Get a string representing the platform-dependent last error
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
new file mode 100644
index 0000000000000..402c62bb35e4f
--- /dev/null
+++ b/src/libstd/sys/common/mod.rs
@@ -0,0 +1,91 @@
+// Copyright 2014 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.
+
+#![allow(missing_doc)]
+#![allow(dead_code)]
+
+use io::{mod, IoError, IoResult};
+use prelude::*;
+use num;
+use sys::{last_error, retry, fs};
+use c_str::CString;
+use path::BytesContainer;
+use collections;
+
+pub mod net;
+
+// common error constructors
+
+pub fn eof() -> IoError {
+    IoError {
+        kind: io::EndOfFile,
+        desc: "end of file",
+        detail: None,
+    }
+}
+
+pub fn timeout(desc: &'static str) -> IoError {
+    IoError {
+        kind: io::TimedOut,
+        desc: desc,
+        detail: None,
+    }
+}
+
+pub fn short_write(n: uint, desc: &'static str) -> IoError {
+    IoError {
+        kind: if n == 0 { io::TimedOut } else { io::ShortWrite(n) },
+        desc: desc,
+        detail: None,
+    }
+}
+
+// unix has nonzero values as errors
+pub fn mkerr_libc<Int: num::Zero>(ret: Int) -> IoResult<()> {
+    if !ret.is_zero() {
+        Err(last_error())
+    } else {
+        Ok(())
+    }
+}
+
+pub fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 {
+    let origamt = data.len();
+    let mut data = data.as_ptr();
+    let mut amt = origamt;
+    while amt > 0 {
+        let ret = retry(|| f(data, amt));
+        if ret == 0 {
+            break
+        } else if ret != -1 {
+            amt -= ret as uint;
+            data = unsafe { data.offset(ret as int) };
+        } else {
+            return ret;
+        }
+    }
+    return (origamt - amt) as i64;
+}
+
+// traits for extracting representations from
+
+pub trait AsFileDesc {
+    fn as_fd(&self) -> &fs::FileDesc;
+}
+
+pub trait ProcessConfig<K: BytesContainer, V: BytesContainer> {
+    fn program(&self) -> &CString;
+    fn args(&self) -> &[CString];
+    fn env(&self) -> Option<&collections::HashMap<K, V>>;
+    fn cwd(&self) -> Option<&CString>;
+    fn uid(&self) -> Option<uint>;
+    fn gid(&self) -> Option<uint>;
+    fn detach(&self) -> bool;
+}
diff --git a/src/libnative/io/c_unix.rs b/src/libstd/sys/unix/c.rs
similarity index 99%
rename from src/libnative/io/c_unix.rs
rename to src/libstd/sys/unix/c.rs
index f1757d367c355..e76f2a2b872db 100644
--- a/src/libnative/io/c_unix.rs
+++ b/src/libstd/sys/unix/c.rs
@@ -11,6 +11,7 @@
 //! C definitions used by libnative that don't belong in liblibc
 
 #![allow(dead_code)]
+#![allow(non_camel_case_types)]
 
 pub use self::select::fd_set;
 pub use self::signal::{sigaction, siginfo, sigset_t};
@@ -106,7 +107,7 @@ mod select {
           target_os = "dragonfly",
           target_os = "linux"))]
 mod select {
-    use std::uint;
+    use uint;
     use libc;
 
     pub const FD_SETSIZE: uint = 1024;
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
new file mode 100644
index 0000000000000..ad5de2dad480d
--- /dev/null
+++ b/src/libstd/sys/unix/mod.rs
@@ -0,0 +1,92 @@
+// Copyright 2014 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.
+
+extern crate libc;
+
+use num;
+use prelude::*;
+use io::{mod, IoResult, IoError};
+
+pub mod fs;
+pub mod os;
+pub mod c;
+
+pub type sock_t = io::file::fd_t;
+pub type wrlen = libc::size_t;
+pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
+
+pub fn last_error() -> IoError {
+    let errno = os::errno() as i32;
+    let mut err = decode_error(errno);
+    err.detail = Some(os::error_string(errno));
+    err
+}
+
+/// Convert an `errno` value into a high-level error variant and description.
+pub fn decode_error(errno: i32) -> IoError {
+    // FIXME: this should probably be a bit more descriptive...
+    let (kind, desc) = match errno {
+        libc::EOF => (io::EndOfFile, "end of file"),
+        libc::ECONNREFUSED => (io::ConnectionRefused, "connection refused"),
+        libc::ECONNRESET => (io::ConnectionReset, "connection reset"),
+        libc::EPERM | libc::EACCES =>
+            (io::PermissionDenied, "permission denied"),
+        libc::EPIPE => (io::BrokenPipe, "broken pipe"),
+        libc::ENOTCONN => (io::NotConnected, "not connected"),
+        libc::ECONNABORTED => (io::ConnectionAborted, "connection aborted"),
+        libc::EADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
+        libc::EADDRINUSE => (io::ConnectionRefused, "address in use"),
+        libc::ENOENT => (io::FileNotFound, "no such file or directory"),
+        libc::EISDIR => (io::InvalidInput, "illegal operation on a directory"),
+        libc::ENOSYS => (io::IoUnavailable, "function not implemented"),
+        libc::EINVAL => (io::InvalidInput, "invalid argument"),
+        libc::ENOTTY =>
+            (io::MismatchedFileTypeForOperation,
+             "file descriptor is not a TTY"),
+        libc::ETIMEDOUT => (io::TimedOut, "operation timed out"),
+        libc::ECANCELED => (io::TimedOut, "operation aborted"),
+
+        // These two constants can have the same value on some systems,
+        // but different values on others, so we can't use a match
+        // clause
+        x if x == libc::EAGAIN || x == libc::EWOULDBLOCK =>
+            (io::ResourceUnavailable, "resource temporarily unavailable"),
+
+        _ => (io::OtherIoError, "unknown error")
+    };
+    IoError { kind: kind, desc: desc, detail: None }
+}
+
+#[inline]
+pub fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
+    let minus_one = -num::one::<I>();
+    loop {
+        let n = f();
+        if n == minus_one && os::errno() == libc::EINTR as int { }
+        else { return n }
+    }
+}
+
+pub fn ms_to_timeval(ms: u64) -> libc::timeval {
+    libc::timeval {
+        tv_sec: (ms / 1000) as libc::time_t,
+        tv_usec: ((ms % 1000) * 1000) as libc::suseconds_t,
+    }
+}
+
+pub fn wouldblock() -> bool {
+    let err = os::errno();
+    err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
+}
+
+pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
+    let set = nb as libc::c_int;
+    super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
+}
diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
new file mode 100644
index 0000000000000..34699eb27c115
--- /dev/null
+++ b/src/libstd/sys/unix/os.rs
@@ -0,0 +1,101 @@
+// Copyright 2014 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 libc;
+use libc::{c_int, c_char};
+use prelude::*;
+
+use os::TMPBUF_SZ;
+
+/// Returns the platform-specific value of errno
+pub fn errno() -> int {
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "freebsd"))]
+    fn errno_location() -> *const c_int {
+        extern {
+            fn __error() -> *const c_int;
+        }
+        unsafe {
+            __error()
+        }
+    }
+
+    #[cfg(target_os = "dragonfly")]
+    fn errno_location() -> *const c_int {
+        extern {
+            fn __dfly_error() -> *const c_int;
+        }
+        unsafe {
+            __dfly_error()
+        }
+    }
+
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn errno_location() -> *const c_int {
+        extern {
+            fn __errno_location() -> *const c_int;
+        }
+        unsafe {
+            __errno_location()
+        }
+    }
+
+    unsafe {
+        (*errno_location()) as int
+    }
+}
+
+/// Get a detailed string description for the given error number
+pub fn error_string(errno: i32) -> String {
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "android",
+              target_os = "freebsd",
+              target_os = "dragonfly"))]
+    fn strerror_r(errnum: c_int, buf: *mut c_char, buflen: libc::size_t)
+                  -> c_int {
+        extern {
+            fn strerror_r(errnum: c_int, buf: *mut c_char,
+                          buflen: libc::size_t) -> c_int;
+        }
+        unsafe {
+            strerror_r(errnum, buf, buflen)
+        }
+    }
+
+    // GNU libc provides a non-compliant version of strerror_r by default
+    // and requires macros to instead use the POSIX compliant variant.
+    // So we just use __xpg_strerror_r which is always POSIX compliant
+    #[cfg(target_os = "linux")]
+    fn strerror_r(errnum: c_int, buf: *mut c_char,
+                  buflen: libc::size_t) -> c_int {
+        extern {
+            fn __xpg_strerror_r(errnum: c_int,
+                                buf: *mut c_char,
+                                buflen: libc::size_t)
+                                -> c_int;
+        }
+        unsafe {
+            __xpg_strerror_r(errnum, buf, buflen)
+        }
+    }
+
+    let mut buf = [0 as c_char, ..TMPBUF_SZ];
+
+    let p = buf.as_mut_ptr();
+    unsafe {
+        if strerror_r(errno as c_int, p, buf.len() as libc::size_t) < 0 {
+            panic!("strerror_r failure");
+        }
+
+        ::string::raw::from_buf(p as *const u8)
+    }
+}
diff --git a/src/libnative/io/c_windows.rs b/src/libstd/sys/windows/c.rs
similarity index 95%
rename from src/libnative/io/c_windows.rs
rename to src/libstd/sys/windows/c.rs
index ee6aa26ede22c..b8e9b1dca3abc 100644
--- a/src/libnative/io/c_windows.rs
+++ b/src/libstd/sys/windows/c.rs
@@ -11,8 +11,11 @@
 //! C definitions used by libnative that don't belong in liblibc
 
 #![allow(overflowing_literals)]
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
 
 use libc;
+use prelude::*;
 
 pub const WSADESCRIPTION_LEN: uint = 256;
 pub const WSASYS_STATUS_LEN: uint = 128;
@@ -127,9 +130,10 @@ extern "system" {
 }
 
 pub mod compat {
-    use std::intrinsics::{atomic_store_relaxed, transmute};
-    use std::iter::Iterator;
+    use intrinsics::{atomic_store_relaxed, transmute};
+    use iter::Iterator;
     use libc::types::os::arch::extra::{LPCWSTR, HMODULE, LPCSTR, LPVOID};
+    use prelude::*;
 
     extern "system" {
         fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
@@ -174,17 +178,17 @@ pub mod compat {
 
                 extern "system" fn thunk($($argname: $argtype),*) -> $rettype {
                     unsafe {
-                        ::io::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
+                        ::sys::c::compat::store_func(&mut ptr as *mut _ as *mut uint,
                                                     stringify!($module),
                                                     stringify!($symbol),
                                                     fallback as uint);
-                        ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+                        ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
                     }
                 }
 
                 extern "system" fn fallback($($argname: $argtype),*) -> $rettype $fallback
 
-                ::std::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
+                ::intrinsics::atomic_load_relaxed(&ptr)($($argname),*)
             }
         );
 
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
new file mode 100644
index 0000000000000..5f4129c14842a
--- /dev/null
+++ b/src/libstd/sys/windows/mod.rs
@@ -0,0 +1,178 @@
+// Copyright 2014 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.
+
+#![allow(missing_doc)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused_imports)]
+#![allow(dead_code)]
+#![allow(unused_unsafe)]
+#![allow(unused_mut)]
+
+extern crate libc;
+
+use num;
+use mem;
+use prelude::*;
+use io::{mod, IoResult, IoError};
+use sync::{Once, ONCE_INIT};
+
+macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
+    static $name: Helper<$m> = Helper {
+        lock: ::rt::mutex::NATIVE_MUTEX_INIT,
+        chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
+        signal: ::cell::UnsafeCell { value: 0 },
+        initialized: ::cell::UnsafeCell { value: false },
+    };
+) )
+
+pub mod fs;
+pub mod os;
+pub mod c;
+
+pub type sock_t = libc::SOCKET;
+pub type wrlen = libc::c_int;
+pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }
+
+// windows has zero values as errors
+fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
+    if ret == 0 {
+        Err(last_error())
+    } else {
+        Ok(())
+    }
+}
+
+pub fn last_error() -> IoError {
+    let errno = os::errno() as i32;
+    let mut err = decode_error(errno);
+    err.detail = Some(os::error_string(errno));
+    err
+}
+
+pub fn last_net_error() -> IoError {
+    let errno = unsafe { c::WSAGetLastError() as i32 };
+    let mut err = decode_error(errno);
+    err.detail = Some(os::error_string(errno));
+    err
+}
+
+pub fn last_gai_error(_errno: i32) -> IoError {
+    last_net_error()
+}
+
+/// Convert an `errno` value into a high-level error variant and description.
+pub fn decode_error(errno: i32) -> IoError {
+    let (kind, desc) = match errno {
+        libc::EOF => (io::EndOfFile, "end of file"),
+        libc::ERROR_NO_DATA => (io::BrokenPipe, "the pipe is being closed"),
+        libc::ERROR_FILE_NOT_FOUND => (io::FileNotFound, "file not found"),
+        libc::ERROR_INVALID_NAME => (io::InvalidInput, "invalid file name"),
+        libc::WSAECONNREFUSED => (io::ConnectionRefused, "connection refused"),
+        libc::WSAECONNRESET => (io::ConnectionReset, "connection reset"),
+        libc::ERROR_ACCESS_DENIED | libc::WSAEACCES =>
+            (io::PermissionDenied, "permission denied"),
+        libc::WSAEWOULDBLOCK => {
+            (io::ResourceUnavailable, "resource temporarily unavailable")
+        }
+        libc::WSAENOTCONN => (io::NotConnected, "not connected"),
+        libc::WSAECONNABORTED => (io::ConnectionAborted, "connection aborted"),
+        libc::WSAEADDRNOTAVAIL => (io::ConnectionRefused, "address not available"),
+        libc::WSAEADDRINUSE => (io::ConnectionRefused, "address in use"),
+        libc::ERROR_BROKEN_PIPE => (io::EndOfFile, "the pipe has ended"),
+        libc::ERROR_OPERATION_ABORTED =>
+            (io::TimedOut, "operation timed out"),
+        libc::WSAEINVAL => (io::InvalidInput, "invalid argument"),
+        libc::ERROR_CALL_NOT_IMPLEMENTED =>
+            (io::IoUnavailable, "function not implemented"),
+        libc::ERROR_INVALID_HANDLE =>
+            (io::MismatchedFileTypeForOperation,
+             "invalid handle provided to function"),
+        libc::ERROR_NOTHING_TO_TERMINATE =>
+            (io::InvalidInput, "no process to kill"),
+
+        // libuv maps this error code to EISDIR. we do too. if it is found
+        // to be incorrect, we can add in some more machinery to only
+        // return this message when ERROR_INVALID_FUNCTION after certain
+        // Windows calls.
+        libc::ERROR_INVALID_FUNCTION => (io::InvalidInput,
+                                         "illegal operation on a directory"),
+
+        _ => (io::OtherIoError, "unknown error")
+    };
+    IoError { kind: kind, desc: desc, detail: None }
+}
+
+pub fn decode_error_detailed(errno: i32) -> IoError {
+    let mut err = decode_error(errno);
+    err.detail = Some(os::error_string(errno));
+    err
+}
+
+#[inline]
+pub fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
+
+pub fn ms_to_timeval(ms: u64) -> libc::timeval {
+    libc::timeval {
+        tv_sec: (ms / 1000) as libc::c_long,
+        tv_usec: ((ms % 1000) * 1000) as libc::c_long,
+    }
+}
+
+pub fn wouldblock() -> bool {
+    let err = os::errno();
+    err == libc::WSAEWOULDBLOCK as uint
+}
+
+pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
+    let mut set = nb as libc::c_ulong;
+    if unsafe { c::ioctlsocket(fd, c::FIONBIO, &mut set) != 0 } {
+        Err(last_error())
+    } else {
+        Ok(())
+    }
+}
+
+// FIXME: call this
+pub fn init_net() {
+    unsafe {
+        static START: Once = ONCE_INIT;
+
+        START.doit(|| {
+            let mut data: c::WSADATA = mem::zeroed();
+            let ret = c::WSAStartup(0x202, // version 2.2
+                                    &mut data);
+            assert_eq!(ret, 0);
+        });
+    }
+}
+
+pub fn unimpl() -> IoError {
+    IoError {
+        kind: io::IoUnavailable,
+        desc: "operation is not implemented",
+        detail: None,
+    }
+}
+
+pub fn to_utf16(s: Option<&str>) -> IoResult<Vec<u16>> {
+    match s {
+        Some(s) => Ok({
+            let mut s = s.utf16_units().collect::<Vec<u16>>();
+            s.push(0);
+            s
+        }),
+        None => Err(IoError {
+            kind: io::InvalidInput,
+            desc: "valid unicode input required",
+            detail: None
+        })
+    }
+}
diff --git a/src/libstd/sys/windows/os.rs b/src/libstd/sys/windows/os.rs
new file mode 100644
index 0000000000000..aaa1aaf632794
--- /dev/null
+++ b/src/libstd/sys/windows/os.rs
@@ -0,0 +1,103 @@
+// Copyright 2014 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.
+
+// FIXME: move various extern bindings from here into liblibc or
+// something similar
+
+use libc;
+use libc::{c_int, c_char, c_void};
+use prelude::*;
+use io::{IoResult, IoError};
+use sys::fs::FileDesc;
+use ptr;
+
+use os::TMPBUF_SZ;
+
+pub fn errno() -> uint {
+    use libc::types::os::arch::extra::DWORD;
+
+    #[link_name = "kernel32"]
+    extern "system" {
+        fn GetLastError() -> DWORD;
+    }
+
+    unsafe {
+        GetLastError() as uint
+    }
+}
+
+/// Get a detailed string description for the given error number
+pub fn error_string(errnum: i32) -> String {
+    use libc::types::os::arch::extra::DWORD;
+    use libc::types::os::arch::extra::LPWSTR;
+    use libc::types::os::arch::extra::LPVOID;
+    use libc::types::os::arch::extra::WCHAR;
+
+    #[link_name = "kernel32"]
+    extern "system" {
+        fn FormatMessageW(flags: DWORD,
+                          lpSrc: LPVOID,
+                          msgId: DWORD,
+                          langId: DWORD,
+                          buf: LPWSTR,
+                          nsize: DWORD,
+                          args: *const c_void)
+                          -> DWORD;
+    }
+
+    static FORMAT_MESSAGE_FROM_SYSTEM: DWORD = 0x00001000;
+    static FORMAT_MESSAGE_IGNORE_INSERTS: DWORD = 0x00000200;
+
+    // This value is calculated from the macro
+    // MAKELANGID(LANG_SYSTEM_DEFAULT, SUBLANG_SYS_DEFAULT)
+    let langId = 0x0800 as DWORD;
+
+    let mut buf = [0 as WCHAR, ..TMPBUF_SZ];
+
+    unsafe {
+        let res = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM |
+                                 FORMAT_MESSAGE_IGNORE_INSERTS,
+                                 ptr::null_mut(),
+                                 errnum as DWORD,
+                                 langId,
+                                 buf.as_mut_ptr(),
+                                 buf.len() as DWORD,
+                                 ptr::null());
+        if res == 0 {
+            // Sometimes FormatMessageW can fail e.g. system doesn't like langId,
+            let fm_err = errno();
+            return format!("OS Error {} (FormatMessageW() returned error {})", errnum, fm_err);
+        }
+
+        let msg = String::from_utf16(::str::truncate_utf16_at_nul(buf));
+        match msg {
+            Some(msg) => format!("OS Error {}: {}", errnum, msg),
+            None => format!("OS Error {} (FormatMessageW() returned invalid UTF-16)", errnum),
+        }
+    }
+}
+
+pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
+    // Windows pipes work subtly differently than unix pipes, and their
+    // inheritance has to be handled in a different way that I do not
+    // fully understand. Here we explicitly make the pipe non-inheritable,
+    // which means to pass it to a subprocess they need to be duplicated
+    // first, as in std::run.
+    let mut fds = [0, ..2];
+    match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
+                     (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
+        0 => {
+            assert!(fds[0] != -1 && fds[0] != 0);
+            assert!(fds[1] != -1 && fds[1] != 0);
+            Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
+        }
+        _ => Err(IoError::last_error()),
+    }
+}

From 16470cf01b688c576f47b93bdb4af88db33cf1e1 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Thu, 9 Oct 2014 16:40:05 -0700
Subject: [PATCH 02/11] Remove signal handling.

Since signal handling was only implemented through librustuv, which is
now gone, there's no reason to even provide the API.

[breaking-change]
---
 src/libnative/io/mod.rs | 4 ----
 src/librustrt/rtio.rs   | 2 --
 2 files changed, 6 deletions(-)

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 90f8f6c214e78..a541712e17f70 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -301,8 +301,4 @@ impl rtio::IoFactory for IoFactory {
             })
         }
     }
-    fn signal(&mut self, _signal: int, _cb: Box<rtio::Callback>)
-              -> IoResult<Box<rtio::RtioSignal + Send>> {
-        Err(unimpl())
-    }
 }
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index a08bc6976025d..e9bee19b4982b 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -233,8 +233,6 @@ pub trait IoFactory {
     fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<RtioPipe + Send>>;
     fn tty_open(&mut self, fd: c_int, readable: bool)
             -> IoResult<Box<RtioTTY + Send>>;
-    fn signal(&mut self, signal: int, cb: Box<Callback + Send>)
-        -> IoResult<Box<RtioSignal + Send>>;
 }
 
 pub trait RtioTcpListener : RtioSocket {

From 0c1e1ff1e300868a29405a334e65eae690df971d Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Tue, 30 Sep 2014 17:34:14 -0700
Subject: [PATCH 03/11] Runtime removal: refactor fs

This moves the filesystem implementation from libnative into the new
`sys` modules, refactoring along the way and hooking into `std::io::fs`.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/file_unix.rs                 | 554 ------------------
 src/libnative/io/mod.rs                       |  83 ---
 src/libnative/io/timer_unix.rs                |   2 +-
 src/librustrt/rtio.rs                         |  86 ---
 src/libstd/io/fs.rs                           | 304 +++-------
 src/libstd/io/mod.rs                          |   1 +
 src/libstd/io/stdio.rs                        |  14 +-
 src/libstd/platform_imp/unix/fs.rs            | 411 +++++++++++++
 .../platform_imp/windows/fs.rs}               | 373 +++++-------
 9 files changed, 662 insertions(+), 1166 deletions(-)
 delete mode 100644 src/libnative/io/file_unix.rs
 create mode 100644 src/libstd/platform_imp/unix/fs.rs
 rename src/{libnative/io/file_windows.rs => libstd/platform_imp/windows/fs.rs} (52%)

diff --git a/src/libnative/io/file_unix.rs b/src/libnative/io/file_unix.rs
deleted file mode 100644
index f616295c73d1b..0000000000000
--- a/src/libnative/io/file_unix.rs
+++ /dev/null
@@ -1,554 +0,0 @@
-// Copyright 2013-2014 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.
-
-//! Blocking posix-based file I/O
-
-use alloc::arc::Arc;
-use libc::{mod, c_int, c_void};
-use std::c_str::CString;
-use std::mem;
-use std::rt::rtio::{mod, IoResult};
-
-use io::{retry, keep_going};
-use io::util;
-
-pub type fd_t = libc::c_int;
-
-struct Inner {
-    fd: fd_t,
-    close_on_drop: bool,
-}
-
-pub struct FileDesc {
-    inner: Arc<Inner>
-}
-
-impl FileDesc {
-    /// Create a `FileDesc` from an open C file descriptor.
-    ///
-    /// The `FileDesc` will take ownership of the specified file descriptor and
-    /// close it upon destruction if the `close_on_drop` flag is true, otherwise
-    /// it will not close the file descriptor when this `FileDesc` is dropped.
-    ///
-    /// Note that all I/O operations done on this object will be *blocking*, but
-    /// they do not require the runtime to be active.
-    pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
-        FileDesc { inner: Arc::new(Inner {
-            fd: fd,
-            close_on_drop: close_on_drop
-        }) }
-    }
-
-    // FIXME(#10465) these functions should not be public, but anything in
-    //               native::io wanting to use them is forced to have all the
-    //               rtio traits in scope
-    pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        let ret = retry(|| unsafe {
-            libc::read(self.fd(),
-                       buf.as_mut_ptr() as *mut libc::c_void,
-                       buf.len() as libc::size_t)
-        });
-        if ret == 0 {
-            Err(util::eof())
-        } else if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as uint)
-        }
-    }
-    pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let ret = keep_going(buf, |buf, len| {
-            unsafe {
-                libc::write(self.fd(), buf as *const libc::c_void,
-                            len as libc::size_t) as i64
-            }
-        });
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(())
-        }
-    }
-
-    pub fn fd(&self) -> fd_t { self.inner.fd }
-}
-
-impl rtio::RtioFileStream for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        self.inner_read(buf).map(|i| i as int)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        match retry(|| unsafe {
-            libc::pread(self.fd(), buf.as_ptr() as *mut _,
-                        buf.len() as libc::size_t,
-                        offset as libc::off_t)
-        }) {
-            -1 => Err(super::last_error()),
-            n => Ok(n as int)
-        }
-    }
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe {
-            libc::pwrite(self.fd(), buf.as_ptr() as *const _,
-                         buf.len() as libc::size_t, offset as libc::off_t)
-        }))
-    }
-    fn seek(&mut self, pos: i64, whence: rtio::SeekStyle) -> IoResult<u64> {
-        let whence = match whence {
-            rtio::SeekSet => libc::SEEK_SET,
-            rtio::SeekEnd => libc::SEEK_END,
-            rtio::SeekCur => libc::SEEK_CUR,
-        };
-        let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn tell(&self) -> IoResult<u64> {
-        let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn fsync(&mut self) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
-    }
-    fn datasync(&mut self) -> IoResult<()> {
-        return super::mkerr_libc(os_datasync(self.fd()));
-
-        #[cfg(any(target_os = "macos", target_os = "ios"))]
-        fn os_datasync(fd: c_int) -> c_int {
-            unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
-        }
-        #[cfg(target_os = "linux")]
-        fn os_datasync(fd: c_int) -> c_int {
-            retry(|| unsafe { libc::fdatasync(fd) })
-        }
-        #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
-        fn os_datasync(fd: c_int) -> c_int {
-            retry(|| unsafe { libc::fsync(fd) })
-        }
-    }
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe {
-            libc::ftruncate(self.fd(), offset as libc::off_t)
-        }))
-    }
-
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
-        let mut stat: libc::stat = unsafe { mem::zeroed() };
-        match unsafe { libc::fstat(self.fd(), &mut stat) } {
-            0 => Ok(mkstat(&stat)),
-            _ => Err(super::last_error()),
-        }
-    }
-}
-
-impl rtio::RtioPipe for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
-    }
-
-    // Only supported on named pipes currently. Note that this doesn't have an
-    // impact on the std::io primitives, this is never called via
-    // std::io::PipeStream. If the functionality is exposed in the future, then
-    // these methods will need to be implemented.
-    fn close_read(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn close_write(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn set_timeout(&mut self, _t: Option<u64>) {}
-    fn set_read_timeout(&mut self, _t: Option<u64>) {}
-    fn set_write_timeout(&mut self, _t: Option<u64>) {}
-}
-
-impl rtio::RtioTTY for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
-        Err(super::unimpl())
-    }
-    fn isatty(&self) -> bool { false }
-}
-
-impl Drop for Inner {
-    fn drop(&mut self) {
-        // closing stdio file handles makes no sense, so never do it. Also, note
-        // that errors are ignored when closing a file descriptor. The reason
-        // for this is that if an error occurs we don't actually know if the
-        // file descriptor was closed or not, and if we retried (for something
-        // like EINTR), we might close another valid file descriptor (opened
-        // after we closed ours.
-        if self.close_on_drop && self.fd > libc::STDERR_FILENO {
-            let n = unsafe { libc::close(self.fd) };
-            if n != 0 {
-                println!("error {} when closing file descriptor {}", n,
-                         self.fd);
-            }
-        }
-    }
-}
-
-pub struct CFile {
-    file: *mut libc::FILE,
-    fd: FileDesc,
-}
-
-impl CFile {
-    /// Create a `CFile` from an open `FILE` pointer.
-    ///
-    /// The `CFile` takes ownership of the `FILE` pointer and will close it upon
-    /// destruction.
-    pub fn new(file: *mut libc::FILE) -> CFile {
-        CFile {
-            file: file,
-            fd: FileDesc::new(unsafe { libc::fileno(file) }, false)
-        }
-    }
-
-    pub fn flush(&mut self) -> IoResult<()> {
-        super::mkerr_libc(retry(|| unsafe { libc::fflush(self.file) }))
-    }
-}
-
-impl rtio::RtioFileStream for CFile {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        let ret = keep_going(buf, |buf, len| {
-            unsafe {
-                libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
-                            self.file) as i64
-            }
-        });
-        if ret == 0 {
-            Err(util::eof())
-        } else if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as int)
-        }
-    }
-
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let ret = keep_going(buf, |buf, len| {
-            unsafe {
-                libc::fwrite(buf as *const libc::c_void, 1, len as libc::size_t,
-                            self.file) as i64
-            }
-        });
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(())
-        }
-    }
-
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        self.flush().and_then(|()| self.fd.pread(buf, offset))
-    }
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.pwrite(buf, offset))
-    }
-    fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
-        let whence = match style {
-            rtio::SeekSet => libc::SEEK_SET,
-            rtio::SeekEnd => libc::SEEK_END,
-            rtio::SeekCur => libc::SEEK_CUR,
-        };
-        let n = unsafe { libc::fseek(self.file, pos as libc::c_long, whence) };
-        if n < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(n as u64)
-        }
-    }
-    fn tell(&self) -> IoResult<u64> {
-        let ret = unsafe { libc::ftell(self.file) };
-        if ret < 0 {
-            Err(super::last_error())
-        } else {
-            Ok(ret as u64)
-        }
-    }
-    fn fsync(&mut self) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.fsync())
-    }
-    fn datasync(&mut self) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.datasync())
-    }
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
-        self.flush().and_then(|()| self.fd.truncate(offset))
-    }
-
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
-        self.flush().and_then(|()| self.fd.fstat())
-    }
-}
-
-impl Drop for CFile {
-    fn drop(&mut self) {
-        unsafe { let _ = libc::fclose(self.file); }
-    }
-}
-
-pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
-    -> IoResult<FileDesc>
-{
-    let flags = match fm {
-        rtio::Open => 0,
-        rtio::Append => libc::O_APPEND,
-        rtio::Truncate => libc::O_TRUNC,
-    };
-    // Opening with a write permission must silently create the file.
-    let (flags, mode) = match fa {
-        rtio::Read => (flags | libc::O_RDONLY, 0),
-        rtio::Write => (flags | libc::O_WRONLY | libc::O_CREAT,
-                        libc::S_IRUSR | libc::S_IWUSR),
-        rtio::ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
-                            libc::S_IRUSR | libc::S_IWUSR),
-    };
-
-    match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
-        -1 => Err(super::last_error()),
-        fd => Ok(FileDesc::new(fd, true)),
-    }
-}
-
-pub fn mkdir(p: &CString, mode: uint) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
-}
-
-pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
-    use libc::{dirent_t};
-    use libc::{opendir, readdir_r, closedir};
-
-    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
-        let root = unsafe { CString::new(root.as_ptr(), false) };
-        let root = Path::new(root);
-
-        dirs.into_iter().filter(|path| {
-            path.as_vec() != b"." && path.as_vec() != b".."
-        }).map(|path| root.join(path).to_c_str()).collect()
-    }
-
-    extern {
-        fn rust_dirent_t_size() -> libc::c_int;
-        fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
-    }
-
-    let size = unsafe { rust_dirent_t_size() };
-    let mut buf = Vec::<u8>::with_capacity(size as uint);
-    let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
-
-    let dir_ptr = unsafe {opendir(p.as_ptr())};
-
-    if dir_ptr as uint != 0 {
-        let mut paths = vec!();
-        let mut entry_ptr = 0 as *mut dirent_t;
-        while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
-            if entry_ptr.is_null() { break }
-            let cstr = unsafe {
-                CString::new(rust_list_dir_val(entry_ptr), false)
-            };
-            paths.push(Path::new(cstr));
-        }
-        assert_eq!(unsafe { closedir(dir_ptr) }, 0);
-        Ok(prune(p, paths))
-    } else {
-        Err(super::last_error())
-    }
-}
-
-pub fn unlink(p: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
-}
-
-pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::rename(old.as_ptr(), new.as_ptr()) })
-}
-
-pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
-    super::mkerr_libc(retry(|| unsafe {
-        libc::chmod(p.as_ptr(), mode as libc::mode_t)
-    }))
-}
-
-pub fn rmdir(p: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
-}
-
-pub fn chown(p: &CString, uid: int, gid: int) -> IoResult<()> {
-    super::mkerr_libc(retry(|| unsafe {
-        libc::chown(p.as_ptr(), uid as libc::uid_t,
-                    gid as libc::gid_t)
-    }))
-}
-
-pub fn readlink(p: &CString) -> IoResult<CString> {
-    let p = p.as_ptr();
-    let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
-    if len == -1 {
-        len = 1024; // FIXME: read PATH_MAX from C ffi?
-    }
-    let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
-    match unsafe {
-        libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
-                       len as libc::size_t) as libc::c_int
-    } {
-        -1 => Err(super::last_error()),
-        n => {
-            assert!(n > 0);
-            unsafe { buf.set_len(n as uint); }
-            Ok(buf.as_slice().to_c_str())
-        }
-    }
-}
-
-pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
-}
-
-pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
-    super::mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
-}
-
-fn mkstat(stat: &libc::stat) -> rtio::FileStat {
-    // FileStat times are in milliseconds
-    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
-
-    #[cfg(not(any(target_os = "linux", target_os = "android")))]
-    fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn flags(_stat: &libc::stat) -> u64 { 0 }
-
-    #[cfg(not(any(target_os = "linux", target_os = "android")))]
-    fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn gen(_stat: &libc::stat) -> u64 { 0 }
-
-    rtio::FileStat {
-        size: stat.st_size as u64,
-        kind: stat.st_mode as u64,
-        perm: stat.st_mode as u64,
-        created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
-        modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
-        accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
-        device: stat.st_dev as u64,
-        inode: stat.st_ino as u64,
-        rdev: stat.st_rdev as u64,
-        nlink: stat.st_nlink as u64,
-        uid: stat.st_uid as u64,
-        gid: stat.st_gid as u64,
-        blksize: stat.st_blksize as u64,
-        blocks: stat.st_blocks as u64,
-        flags: flags(stat),
-        gen: gen(stat),
-    }
-}
-
-pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
-    let mut stat: libc::stat = unsafe { mem::zeroed() };
-    match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
-        0 => Ok(mkstat(&stat)),
-        _ => Err(super::last_error()),
-    }
-}
-
-pub fn lstat(p: &CString) -> IoResult<rtio::FileStat> {
-    let mut stat: libc::stat = unsafe { mem::zeroed() };
-    match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
-        0 => Ok(mkstat(&stat)),
-        _ => Err(super::last_error()),
-    }
-}
-
-pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
-    let buf = libc::utimbuf {
-        actime: (atime / 1000) as libc::time_t,
-        modtime: (mtime / 1000) as libc::time_t,
-    };
-    super::mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
-}
-
-#[cfg(test)]
-mod tests {
-    use super::{CFile, FileDesc};
-    use libc;
-    use std::os;
-    use std::rt::rtio::{RtioFileStream, SeekSet};
-
-    #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
-    #[test]
-    fn test_file_desc() {
-        // Run this test with some pipes so we don't have to mess around with
-        // opening or closing files.
-        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
-        let mut reader = FileDesc::new(reader, true);
-        let mut writer = FileDesc::new(writer, true);
-
-        writer.inner_write(b"test").ok().unwrap();
-        let mut buf = [0u8, ..4];
-        match reader.inner_read(buf) {
-            Ok(4) => {
-                assert_eq!(buf[0], 't' as u8);
-                assert_eq!(buf[1], 'e' as u8);
-                assert_eq!(buf[2], 's' as u8);
-                assert_eq!(buf[3], 't' as u8);
-            }
-            r => panic!("invalid read: {}", r),
-        }
-
-        assert!(writer.inner_read(buf).is_err());
-        assert!(reader.inner_write(buf).is_err());
-    }
-
-    #[test]
-    fn test_cfile() {
-        unsafe {
-            let f = libc::tmpfile();
-            assert!(!f.is_null());
-            let mut file = CFile::new(f);
-
-            file.write(b"test").ok().unwrap();
-            let mut buf = [0u8, ..4];
-            let _ = file.seek(0, SeekSet).ok().unwrap();
-            match file.read(buf) {
-                Ok(4) => {
-                    assert_eq!(buf[0], 't' as u8);
-                    assert_eq!(buf[1], 'e' as u8);
-                    assert_eq!(buf[2], 's' as u8);
-                    assert_eq!(buf[3], 't' as u8);
-                }
-                r => panic!("invalid read: {}", r)
-            }
-        }
-    }
-}
diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index a541712e17f70..baf58b83dcd25 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -30,7 +30,6 @@ use std::rt::rtio::{mod, IoResult, IoError};
 use std::num;
 
 // Local re-exports
-pub use self::file::FileDesc;
 pub use self::process::Process;
 
 mod helper_thread;
@@ -41,13 +40,6 @@ pub mod net;
 pub mod process;
 mod util;
 
-#[cfg(unix)]
-#[path = "file_unix.rs"]
-pub mod file;
-#[cfg(windows)]
-#[path = "file_windows.rs"]
-pub mod file;
-
 #[cfg(any(target_os = "macos",
           target_os = "ios",
           target_os = "freebsd",
@@ -92,25 +84,6 @@ fn last_error() -> IoError {
     }
 }
 
-// unix has nonzero values as errors
-fn mkerr_libc <Int: num::Zero>(ret: Int) -> IoResult<()> {
-    if !ret.is_zero() {
-        Err(last_error())
-    } else {
-        Ok(())
-    }
-}
-
-// windows has zero values as errors
-#[cfg(windows)]
-fn mkerr_winbool(ret: libc::c_int) -> IoResult<()> {
-    if ret == 0 {
-        Err(last_error())
-    } else {
-        Ok(())
-    }
-}
-
 #[cfg(windows)]
 #[inline]
 fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
@@ -199,62 +172,6 @@ impl rtio::IoFactory for IoFactory {
         addrinfo::GetAddrInfoRequest::run(host, servname, hint)
     }
 
-    // filesystem operations
-    fn fs_from_raw_fd(&mut self, fd: c_int, close: rtio::CloseBehavior)
-                      -> Box<rtio::RtioFileStream + Send> {
-        let close = match close {
-            rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
-            rtio::DontClose => false
-        };
-        box file::FileDesc::new(fd, close) as Box<rtio::RtioFileStream + Send>
-    }
-    fn fs_open(&mut self, path: &CString, fm: rtio::FileMode,
-               fa: rtio::FileAccess)
-        -> IoResult<Box<rtio::RtioFileStream + Send>>
-    {
-        file::open(path, fm, fa).map(|fd| box fd as Box<rtio::RtioFileStream + Send>)
-    }
-    fn fs_unlink(&mut self, path: &CString) -> IoResult<()> {
-        file::unlink(path)
-    }
-    fn fs_stat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
-        file::stat(path)
-    }
-    fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()> {
-        file::mkdir(path, mode)
-    }
-    fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()> {
-        file::chmod(path, mode)
-    }
-    fn fs_rmdir(&mut self, path: &CString) -> IoResult<()> {
-        file::rmdir(path)
-    }
-    fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()> {
-        file::rename(path, to)
-    }
-    fn fs_readdir(&mut self, path: &CString, _flags: c_int) -> IoResult<Vec<CString>> {
-        file::readdir(path)
-    }
-    fn fs_lstat(&mut self, path: &CString) -> IoResult<rtio::FileStat> {
-        file::lstat(path)
-    }
-    fn fs_chown(&mut self, path: &CString, uid: int, gid: int) -> IoResult<()> {
-        file::chown(path, uid, gid)
-    }
-    fn fs_readlink(&mut self, path: &CString) -> IoResult<CString> {
-        file::readlink(path)
-    }
-    fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
-        file::symlink(src, dst)
-    }
-    fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()> {
-        file::link(src, dst)
-    }
-    fn fs_utime(&mut self, src: &CString, atime: u64,
-                mtime: u64) -> IoResult<()> {
-        file::utime(src, atime, mtime)
-    }
-
     // misc
     fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
         timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
diff --git a/src/libnative/io/timer_unix.rs b/src/libnative/io/timer_unix.rs
index 38895f2a8f96a..c26e2e76cee63 100644
--- a/src/libnative/io/timer_unix.rs
+++ b/src/libnative/io/timer_unix.rs
@@ -56,7 +56,7 @@ use std::sync::atomic;
 use std::comm;
 
 use io::c;
-use io::file::FileDesc;
+use platform_imp::fs::FileDesc;
 use io::helper_thread::Helper;
 
 helper_init!(static HELPER: Helper<Req>)
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index e9bee19b4982b..1f3ef60e6fb2b 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -50,20 +50,6 @@ pub trait RemoteCallback {
     fn fire(&mut self);
 }
 
-/// Description of what to do when a file handle is closed
-pub enum CloseBehavior {
-    /// Do not close this handle when the object is destroyed
-    DontClose,
-    /// Synchronously close the handle, meaning that the task will block when
-    /// the handle is destroyed until it has been fully closed.
-    CloseSynchronously,
-    /// Asynchronously closes a handle, meaning that the task will *not* block
-    /// when the handle is destroyed, but the handle will still get deallocated
-    /// and cleaned up (but this will happen asynchronously on the local event
-    /// loop).
-    CloseAsynchronously,
-}
-
 /// Data needed to spawn a process. Serializes the `std::io::process::Command`
 /// builder.
 pub struct ProcessConfig<'a> {
@@ -202,28 +188,6 @@ pub trait IoFactory {
                           hint: Option<AddrinfoHint>)
                           -> IoResult<Vec<AddrinfoInfo>>;
 
-    // filesystem operations
-    fn fs_from_raw_fd(&mut self, fd: c_int, close: CloseBehavior)
-                      -> Box<RtioFileStream + Send>;
-    fn fs_open(&mut self, path: &CString, fm: FileMode, fa: FileAccess)
-               -> IoResult<Box<RtioFileStream + Send>>;
-    fn fs_unlink(&mut self, path: &CString) -> IoResult<()>;
-    fn fs_stat(&mut self, path: &CString) -> IoResult<FileStat>;
-    fn fs_mkdir(&mut self, path: &CString, mode: uint) -> IoResult<()>;
-    fn fs_chmod(&mut self, path: &CString, mode: uint) -> IoResult<()>;
-    fn fs_rmdir(&mut self, path: &CString) -> IoResult<()>;
-    fn fs_rename(&mut self, path: &CString, to: &CString) -> IoResult<()>;
-    fn fs_readdir(&mut self, path: &CString, flags: c_int) ->
-        IoResult<Vec<CString>>;
-    fn fs_lstat(&mut self, path: &CString) -> IoResult<FileStat>;
-    fn fs_chown(&mut self, path: &CString, uid: int, gid: int) ->
-        IoResult<()>;
-    fn fs_readlink(&mut self, path: &CString) -> IoResult<CString>;
-    fn fs_symlink(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
-    fn fs_link(&mut self, src: &CString, dst: &CString) -> IoResult<()>;
-    fn fs_utime(&mut self, src: &CString, atime: u64, mtime: u64) ->
-        IoResult<()>;
-
     // misc
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
     fn spawn(&mut self, cfg: ProcessConfig)
@@ -296,19 +260,6 @@ pub trait RtioTimer {
     fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
 }
 
-pub trait RtioFileStream {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int>;
-    fn write(&mut self, buf: &[u8]) -> IoResult<()>;
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int>;
-    fn pwrite(&mut self, buf: &[u8], offset: u64) -> IoResult<()>;
-    fn seek(&mut self, pos: i64, whence: SeekStyle) -> IoResult<u64>;
-    fn tell(&self) -> IoResult<u64>;
-    fn fsync(&mut self) -> IoResult<()>;
-    fn datasync(&mut self) -> IoResult<()>;
-    fn truncate(&mut self, offset: i64) -> IoResult<()>;
-    fn fstat(&mut self) -> IoResult<FileStat>;
-}
-
 pub trait RtioProcess {
     fn id(&self) -> libc::pid_t;
     fn kill(&mut self, signal: int) -> IoResult<()>;
@@ -399,43 +350,6 @@ pub enum ProcessExit {
     ExitSignal(int),
 }
 
-pub enum FileMode {
-    Open,
-    Append,
-    Truncate,
-}
-
-pub enum FileAccess {
-    Read,
-    Write,
-    ReadWrite,
-}
-
-pub struct FileStat {
-    pub size: u64,
-    pub kind: u64,
-    pub perm: u64,
-    pub created: u64,
-    pub modified: u64,
-    pub accessed: u64,
-    pub device: u64,
-    pub inode: u64,
-    pub rdev: u64,
-    pub nlink: u64,
-    pub uid: u64,
-    pub gid: u64,
-    pub blksize: u64,
-    pub blocks: u64,
-    pub flags: u64,
-    pub gen: u64,
-}
-
-pub enum SeekStyle {
-    SeekSet,
-    SeekEnd,
-    SeekCur,
-}
-
 pub struct AddrinfoHint {
     pub family: uint,
     pub socktype: uint,
diff --git a/src/libstd/io/fs.rs b/src/libstd/io/fs.rs
index e76046bac059f..5c2a5c3512d32 100644
--- a/src/libstd/io/fs.rs
+++ b/src/libstd/io/fs.rs
@@ -52,28 +52,25 @@ fs::unlink(&path);
 
 */
 
-use c_str::ToCStr;
 use clone::Clone;
 use io::standard_error;
-use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{FilePermission, Write, Open, FileAccess, FileMode};
 use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
-use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+use io::{Read, Truncate, ReadWrite, Append};
 use io::UpdateIoError;
 use io;
 use iter::{Iterator, Extend};
-use kinds::Send;
-use libc;
 use option::{Some, None, Option};
-use boxed::Box;
 use path::{Path, GenericPath};
 use path;
 use result::{Err, Ok};
-use rt::rtio::LocalIo;
-use rt::rtio;
 use slice::SlicePrelude;
 use string::String;
 use vec::Vec;
 
+use sys::fs as fs_imp;
+use sys_common;
+
 /// Unconstrained file access type that exposes read and write operations
 ///
 /// Can be constructed via `File::open()`, `File::create()`, and
@@ -86,11 +83,17 @@ use vec::Vec;
 /// configured at creation time, via the `FileAccess` parameter to
 /// `File::open_mode()`.
 pub struct File {
-    fd: Box<rtio::RtioFileStream + Send>,
+    fd: fs_imp::FileDesc,
     path: Path,
     last_nread: int,
 }
 
+impl sys_common::AsFileDesc for File {
+    fn as_fd(&self) -> &fs_imp::FileDesc {
+        &self.fd
+    }
+}
+
 impl File {
     /// Open a file at `path` in the mode specified by the `mode` and `access`
     /// arguments
@@ -133,26 +136,13 @@ impl File {
     pub fn open_mode(path: &Path,
                      mode: FileMode,
                      access: FileAccess) -> IoResult<File> {
-        let rtio_mode = match mode {
-            Open => rtio::Open,
-            Append => rtio::Append,
-            Truncate => rtio::Truncate,
-        };
-        let rtio_access = match access {
-            Read => rtio::Read,
-            Write => rtio::Write,
-            ReadWrite => rtio::ReadWrite,
-        };
-        let err = LocalIo::maybe_raise(|io| {
-            io.fs_open(&path.to_c_str(), rtio_mode, rtio_access).map(|fd| {
-                File {
-                    path: path.clone(),
-                    fd: fd,
-                    last_nread: -1
-                }
-            })
-        }).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't open file", |e| {
+        fs_imp::open(path, mode, access).map(|fd| {
+            File {
+                path: path.clone(),
+                fd: fd,
+                last_nread: -1
+            }
+        }).update_err("couldn't open file", |e| {
             format!("{}; path={}; mode={}; access={}", e, path.display(),
                 mode_string(mode), access_string(access))
         })
@@ -194,7 +184,7 @@ impl File {
     /// ```
     pub fn create(path: &Path) -> IoResult<File> {
         File::open_mode(path, Truncate, Write)
-            .update_desc("couldn't create file")
+             .update_desc("couldn't create file")
     }
 
     /// Returns the original path which was used to open this file.
@@ -206,9 +196,9 @@ impl File {
     /// device. This will flush any internal buffers necessary to perform this
     /// operation.
     pub fn fsync(&mut self) -> IoResult<()> {
-        let err = self.fd.fsync().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't fsync file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.fsync()
+            .update_err("couldn't fsync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     /// This function is similar to `fsync`, except that it may not synchronize
@@ -216,9 +206,9 @@ impl File {
     /// must synchronize content, but don't need the metadata on disk. The goal
     /// of this method is to reduce disk operations.
     pub fn datasync(&mut self) -> IoResult<()> {
-        let err = self.fd.datasync().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't datasync file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.datasync()
+            .update_err("couldn't datasync file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     /// Either truncates or extends the underlying file, updating the size of
@@ -230,10 +220,9 @@ impl File {
     /// will be extended to `size` and have all of the intermediate data filled
     /// in with 0s.
     pub fn truncate(&mut self, size: i64) -> IoResult<()> {
-        let err = self.fd.truncate(size).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't truncate file", |e| {
-            format!("{}; path={}; size={}", e, self.path.display(), size)
-        })
+        self.fd.truncate(size)
+            .update_err("couldn't truncate file", |e|
+                format!("{}; path={}; size={}", e, self.path.display(), size))
     }
 
     /// Returns true if the stream has reached the end of the file.
@@ -251,12 +240,9 @@ impl File {
 
     /// Queries information about the underlying file.
     pub fn stat(&mut self) -> IoResult<FileStat> {
-        let err = match self.fd.fstat() {
-            Ok(s) => Ok(from_rtio(s)),
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        };
-        err.update_err("couldn't fstat file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.fstat()
+            .update_err("couldn't fstat file", |e|
+                format!("{}; path={}", e, self.path.display()))
     }
 }
 
@@ -282,41 +268,9 @@ impl File {
 /// user lacks permissions to remove the file, or if some other filesystem-level
 /// error occurs.
 pub fn unlink(path: &Path) -> IoResult<()> {
-    return match do_unlink(path) {
-        Ok(()) => Ok(()),
-        Err(e) => {
-            // On unix, a readonly file can be successfully removed. On windows,
-            // however, it cannot. To keep the two platforms in line with
-            // respect to their behavior, catch this case on windows, attempt to
-            // change it to read-write, and then remove the file.
-            if cfg!(windows) && e.kind == io::PermissionDenied {
-                let stat = match stat(path) {
-                    Ok(stat) => stat,
-                    Err(..) => return Err(e),
-                };
-                if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
-
-                match chmod(path, stat.perm | io::USER_WRITE) {
-                    Ok(()) => do_unlink(path),
-                    Err(..) => {
-                        // Try to put it back as we found it
-                        let _ = chmod(path, stat.perm);
-                        Err(e)
-                    }
-                }
-            } else {
-                Err(e)
-            }
-        }
-    };
-
-    fn do_unlink(path: &Path) -> IoResult<()> {
-        let err = LocalIo::maybe_raise(|io| {
-            io.fs_unlink(&path.to_c_str())
-        }).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't unlink path",
-                       |e| format!("{}; path={}", e, path.display()))
-    }
+    fs_imp::unlink(path)
+           .update_err("couldn't unlink path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Given a path, query the file system to get information about a file,
@@ -341,12 +295,9 @@ pub fn unlink(path: &Path) -> IoResult<()> {
 /// to perform a `stat` call on the given `path` or if there is no entry in the
 /// filesystem at the provided path.
 pub fn stat(path: &Path) -> IoResult<FileStat> {
-    let err = match LocalIo::maybe_raise(|io| io.fs_stat(&path.to_c_str())) {
-        Ok(s) => Ok(from_rtio(s)),
-        Err(e) => Err(IoError::from_rtio_error(e)),
-    };
-    err.update_err("couldn't stat path",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::stat(path)
+           .update_err("couldn't stat path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Perform the same operation as the `stat` function, except that this
@@ -358,53 +309,9 @@ pub fn stat(path: &Path) -> IoResult<FileStat> {
 ///
 /// See `stat`
 pub fn lstat(path: &Path) -> IoResult<FileStat> {
-    let err = match LocalIo::maybe_raise(|io| io.fs_lstat(&path.to_c_str())) {
-        Ok(s) => Ok(from_rtio(s)),
-        Err(e) => Err(IoError::from_rtio_error(e)),
-    };
-    err.update_err("couldn't lstat path",
-                   |e| format!("{}; path={}", e, path.display()))
-}
-
-fn from_rtio(s: rtio::FileStat) -> FileStat {
-    #[cfg(windows)]
-    type Mode = libc::c_int;
-    #[cfg(unix)]
-    type Mode = libc::mode_t;
-
-    let rtio::FileStat {
-        size, kind, perm, created, modified,
-        accessed, device, inode, rdev,
-        nlink, uid, gid, blksize, blocks, flags, gen
-    } = s;
-
-    FileStat {
-        size: size,
-        kind: match (kind as Mode) & libc::S_IFMT {
-            libc::S_IFREG => io::TypeFile,
-            libc::S_IFDIR => io::TypeDirectory,
-            libc::S_IFIFO => io::TypeNamedPipe,
-            libc::S_IFBLK => io::TypeBlockSpecial,
-            libc::S_IFLNK => io::TypeSymlink,
-            _ => io::TypeUnknown,
-        },
-        perm: FilePermission::from_bits_truncate(perm as u32),
-        created: created,
-        modified: modified,
-        accessed: accessed,
-        unstable: UnstableFileStat {
-            device: device,
-            inode: inode,
-            rdev: rdev,
-            nlink: nlink,
-            uid: uid,
-            gid: gid,
-            blksize: blksize,
-            blocks: blocks,
-            flags: flags,
-            gen: gen,
-        },
-    }
+    fs_imp::lstat(path)
+           .update_err("couldn't lstat path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Rename a file or directory to a new name.
@@ -424,12 +331,9 @@ fn from_rtio(s: rtio::FileStat) -> FileStat {
 /// the process lacks permissions to view the contents, or if some other
 /// intermittent I/O error occurs.
 pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_rename(&from.to_c_str(), &to.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't rename path", |e| {
-        format!("{}; from={}; to={}", e, from.display(), to.display())
-    })
+    fs_imp::rename(from, to)
+           .update_err("couldn't rename path", |e|
+               format!("{}; from={}; to={}", e, from.display(), to.display()))
 }
 
 /// Copies the contents of one file to another. This function will also
@@ -462,8 +366,9 @@ pub fn rename(from: &Path, to: &Path) -> IoResult<()> {
 /// being created and then destroyed by this operation.
 pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
     fn update_err<T>(result: IoResult<T>, from: &Path, to: &Path) -> IoResult<T> {
-        result.update_err("couldn't copy path",
-            |e| format!("{}; from={}; to={}", e, from.display(), to.display()))
+        result.update_err("couldn't copy path", |e| {
+            format!("{}; from={}; to={}", e, from.display(), to.display())
+        })
     }
 
     if !from.is_file() {
@@ -512,45 +417,33 @@ pub fn copy(from: &Path, to: &Path) -> IoResult<()> {
 /// the process lacks permissions to change the attributes of the file, or if
 /// some other I/O error is encountered.
 pub fn chmod(path: &Path, mode: io::FilePermission) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_chmod(&path.to_c_str(), mode.bits() as uint)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't chmod path", |e| {
-        format!("{}; path={}; mode={}", e, path.display(), mode)
-    })
+    fs_imp::chmod(path, mode.bits() as uint)
+           .update_err("couldn't chmod path", |e|
+               format!("{}; path={}; mode={}", e, path.display(), mode))
 }
 
 /// Change the user and group owners of a file at the specified path.
 pub fn chown(path: &Path, uid: int, gid: int) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_chown(&path.to_c_str(), uid, gid)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't chown path", |e| {
-        format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid)
-    })
+    fs_imp::chown(path, uid, gid)
+           .update_err("couldn't chown path", |e|
+               format!("{}; path={}; uid={}; gid={}", e, path.display(), uid, gid))
 }
 
 /// Creates a new hard link on the filesystem. The `dst` path will be a
 /// link pointing to the `src` path. Note that systems often require these
 /// two paths to both be located on the same filesystem.
 pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_link(&src.to_c_str(), &dst.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't link path", |e| {
-        format!("{}; src={}; dest={}", e, src.display(), dst.display())
-    })
+    fs_imp::link(src, dst)
+           .update_err("couldn't link path", |e|
+               format!("{}; src={}; dest={}", e, src.display(), dst.display()))
 }
 
 /// Creates a new symbolic link on the filesystem. The `dst` path will be a
 /// symlink pointing to the `src` path.
 pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_symlink(&src.to_c_str(), &dst.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't symlink path", |e| {
-        format!("{}; src={}; dest={}", e, src.display(), dst.display())
-    })
+    fs_imp::symlink(src, dst)
+           .update_err("couldn't symlink path", |e|
+               format!("{}; src={}; dest={}", e, src.display(), dst.display()))
 }
 
 /// Reads a symlink, returning the file that the symlink points to.
@@ -560,11 +453,9 @@ pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
 /// This function will return an error on failure. Failure conditions include
 /// reading a file that does not exist or reading a file which is not a symlink.
 pub fn readlink(path: &Path) -> IoResult<Path> {
-    let err = LocalIo::maybe_raise(|io| {
-        Ok(Path::new(try!(io.fs_readlink(&path.to_c_str()))))
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't resolve symlink for path",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::readlink(path)
+           .update_err("couldn't resolve symlink for path", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Create a new, empty directory at the provided path
@@ -585,12 +476,9 @@ pub fn readlink(path: &Path) -> IoResult<Path> {
 /// This function will return an error if the user lacks permissions to make a
 /// new directory at the provided `path`, or if the directory already exists.
 pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_mkdir(&path.to_c_str(), mode.bits() as uint)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't create directory", |e| {
-        format!("{}; path={}; mode={}", e, path.display(), mode)
-    })
+    fs_imp::mkdir(path, mode.bits() as uint)
+           .update_err("couldn't create directory", |e|
+               format!("{}; path={}; mode={}", e, path.display(), mode))
 }
 
 /// Remove an existing, empty directory
@@ -610,11 +498,9 @@ pub fn mkdir(path: &Path, mode: FilePermission) -> IoResult<()> {
 /// This function will return an error if the user lacks permissions to remove
 /// the directory at the provided `path`, or if the directory isn't empty.
 pub fn rmdir(path: &Path) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_rmdir(&path.to_c_str())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't remove directory",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::rmdir(path)
+           .update_err("couldn't remove directory", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 /// Retrieve a vector containing all entries within a provided directory
@@ -650,13 +536,9 @@ pub fn rmdir(path: &Path) -> IoResult<()> {
 /// the process lacks permissions to view the contents or if the `path` points
 /// at a non-directory file
 pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
-    let err = LocalIo::maybe_raise(|io| {
-        Ok(try!(io.fs_readdir(&path.to_c_str(), 0)).into_iter().map(|a| {
-            Path::new(a)
-        }).collect())
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't read directory",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::readdir(path)
+           .update_err("couldn't read directory",
+                       |e| format!("{}; path={}", e, path.display()))
 }
 
 /// Returns an iterator which will recursively walk the directory structure
@@ -666,8 +548,7 @@ pub fn readdir(path: &Path) -> IoResult<Vec<Path>> {
 pub fn walk_dir(path: &Path) -> IoResult<Directories> {
     Ok(Directories {
         stack: try!(readdir(path).update_err("couldn't walk directory",
-                                             |e| format!("{}; path={}",
-                                                         e, path.display())))
+                                             |e| format!("{}; path={}", e, path.display())))
     })
 }
 
@@ -681,12 +562,7 @@ impl Iterator<Path> for Directories {
         match self.stack.pop() {
             Some(path) => {
                 if path.is_dir() {
-                    let result = readdir(&path)
-                        .update_err("couldn't advance Directories iterator",
-                                    |e| format!("{}; path={}",
-                                                e, path.display()));
-
-                    match result {
+                    match readdir(&path) {
                         Ok(dirs) => { self.stack.extend(dirs.into_iter()); }
                         Err(..) => {}
                     }
@@ -804,11 +680,9 @@ pub fn rmdir_recursive(path: &Path) -> IoResult<()> {
 /// be in milliseconds.
 // FIXME(#10301) these arguments should not be u64
 pub fn change_file_times(path: &Path, atime: u64, mtime: u64) -> IoResult<()> {
-    let err = LocalIo::maybe_raise(|io| {
-        io.fs_utime(&path.to_c_str(), atime, mtime)
-    }).map_err(IoError::from_rtio_error);
-    err.update_err("couldn't change_file_times",
-                   |e| format!("{}; path={}", e, path.display()))
+    fs_imp::utime(path, atime, mtime)
+           .update_err("couldn't change_file_times", |e|
+               format!("{}; path={}", e, path.display()))
 }
 
 impl Reader for File {
@@ -819,12 +693,11 @@ impl Reader for File {
                                           e, file.path.display()))
         }
 
-        let result = update_err(self.fd.read(buf)
-                                    .map_err(IoError::from_rtio_error), self);
+        let result = update_err(self.fd.read(buf), self);
 
         match result {
             Ok(read) => {
-                self.last_nread = read;
+                self.last_nread = read as int;
                 match read {
                     0 => update_err(Err(standard_error(io::EndOfFile)), self),
                     _ => Ok(read as uint)
@@ -837,32 +710,27 @@ impl Reader for File {
 
 impl Writer for File {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        let err = self.fd.write(buf).map_err(IoError::from_rtio_error);
-        err.update_err("couldn't write to file",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.write(buf)
+            .update_err("couldn't write to file",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 }
 
 impl Seek for File {
     fn tell(&self) -> IoResult<u64> {
-        let err = self.fd.tell().map_err(IoError::from_rtio_error);
-        err.update_err("couldn't retrieve file cursor (`tell`)",
-                       |e| format!("{}; path={}", e, self.path.display()))
+        self.fd.tell()
+            .update_err("couldn't retrieve file cursor (`tell`)",
+                        |e| format!("{}; path={}", e, self.path.display()))
     }
 
     fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<()> {
-        let style = match style {
-            SeekSet => rtio::SeekSet,
-            SeekCur => rtio::SeekCur,
-            SeekEnd => rtio::SeekEnd,
-        };
         let err = match self.fd.seek(pos, style) {
             Ok(_) => {
                 // successful seek resets EOF indicator
                 self.last_nread = -1;
                 Ok(())
             }
-            Err(e) => Err(IoError::from_rtio_error(e)),
+            Err(e) => Err(e),
         };
         err.update_err("couldn't seek in file",
                        |e| format!("{}; path={}", e, self.path.display()))
@@ -942,6 +810,8 @@ fn access_string(access: FileAccess) -> &'static str {
 
 #[cfg(test)]
 #[allow(unused_imports)]
+#[allow(unused_variables)]
+#[allow(unused_mut)]
 mod test {
     use prelude::*;
     use io::{SeekSet, SeekCur, SeekEnd, Read, Open, ReadWrite};
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 78abbb9f80df7..03c073c1477d5 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -316,6 +316,7 @@ impl IoError {
             err.detail = Some(os::error_string(errno).as_slice().chars()
                                  .map(|c| c.to_lowercase()).collect())
         }
+        err
     }
 
     /// Retrieve the last error to occur as a (detailed) IoError.
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 7bae67c0aa6b6..98644cfc7e995 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -36,11 +36,11 @@ use kinds::Send;
 use libc;
 use option::{Option, Some, None};
 use boxed::Box;
+use sys::fs::FileDesc;
 use result::{Ok, Err};
 use rt;
 use rt::local::Local;
 use rt::task::Task;
-use rt::rtio::{DontClose, IoFactory, LocalIo, RtioFileStream, RtioTTY};
 use slice::SlicePrelude;
 use str::StrPrelude;
 use uint;
@@ -75,14 +75,14 @@ use uint;
 //        case pipe also doesn't work, but magically file does!
 enum StdSource {
     TTY(Box<RtioTTY + Send>),
-    File(Box<RtioFileStream + Send>),
+    File(FileDesc),
 }
 
 fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
     LocalIo::maybe_raise(|io| {
         Ok(match io.tty_open(fd, readable) {
             Ok(tty) => f(TTY(tty)),
-            Err(_) => f(File(io.fs_from_raw_fd(fd, DontClose))),
+            Err(_) => f(File(FileDesc::new(fd, false))),
         })
     }).map_err(IoError::from_rtio_error).unwrap()
 }
@@ -278,10 +278,10 @@ impl Reader for StdReader {
                 // print!'d prompt not being shown until after the user hits
                 // enter.
                 flush();
-                tty.read(buf)
+                tty.read(buf).map_err(IoError::from_rtio_error)
             },
             File(ref mut file) => file.read(buf).map(|i| i as uint),
-        }.map_err(IoError::from_rtio_error);
+        };
         match ret {
             // When reading a piped stdin, libuv will return 0-length reads when
             // stdin reaches EOF. For pretty much all other streams it will
@@ -372,9 +372,9 @@ impl Writer for StdWriter {
         let max_size = if cfg!(windows) {8192} else {uint::MAX};
         for chunk in buf.chunks(max_size) {
             try!(match self.inner {
-                TTY(ref mut tty) => tty.write(chunk),
+                TTY(ref mut tty) => tty.write(chunk).map_err(IoError::from_rtio_error),
                 File(ref mut file) => file.write(chunk),
-            }.map_err(IoError::from_rtio_error))
+            })
         }
         Ok(())
     }
diff --git a/src/libstd/platform_imp/unix/fs.rs b/src/libstd/platform_imp/unix/fs.rs
new file mode 100644
index 0000000000000..3dcd99859e8cf
--- /dev/null
+++ b/src/libstd/platform_imp/unix/fs.rs
@@ -0,0 +1,411 @@
+// Copyright 2013-2014 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.
+
+//! Blocking posix-based file I/O
+
+use libc::{mod, c_int, c_void};
+use c_str::CString;
+use mem;
+use io;
+
+use prelude::*;
+
+use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{IoResult, FileStat, SeekStyle, Reader};
+use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+use result::{Ok, Err};
+use sys::retry;
+use sys_common::{keep_going, eof, mkerr_libc};
+
+pub use path::PosixPath as Path;
+
+pub type fd_t = libc::c_int;
+
+pub struct FileDesc {
+    /// The underlying C file descriptor.
+    fd: fd_t,
+
+    /// Whether to close the file descriptor on drop.
+    close_on_drop: bool,
+}
+
+impl FileDesc {
+    pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
+        FileDesc { fd: fd, close_on_drop: close_on_drop }
+    }
+
+    pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
+        let ret = retry(|| unsafe {
+            libc::read(self.fd(),
+                       buf.as_mut_ptr() as *mut libc::c_void,
+                       buf.len() as libc::size_t)
+        });
+        if ret == 0 {
+            Err(eof())
+        } else if ret < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(ret as uint)
+        }
+    }
+    pub fn write(&self, buf: &[u8]) -> IoResult<()> {
+        let ret = keep_going(buf, |buf, len| {
+            unsafe {
+                libc::write(self.fd(), buf as *const libc::c_void,
+                            len as libc::size_t) as i64
+            }
+        });
+        if ret < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(())
+        }
+    }
+
+    pub fn fd(&self) -> fd_t { self.fd }
+
+    pub fn seek(&self, pos: i64, whence: SeekStyle) -> IoResult<u64> {
+        let whence = match whence {
+            SeekSet => libc::SEEK_SET,
+            SeekEnd => libc::SEEK_END,
+            SeekCur => libc::SEEK_CUR,
+        };
+        let n = unsafe { libc::lseek(self.fd(), pos as libc::off_t, whence) };
+        if n < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(n as u64)
+        }
+    }
+
+    pub fn tell(&self) -> IoResult<u64> {
+        let n = unsafe { libc::lseek(self.fd(), 0, libc::SEEK_CUR) };
+        if n < 0 {
+            Err(super::last_error())
+        } else {
+            Ok(n as u64)
+        }
+    }
+
+    pub fn fsync(&self) -> IoResult<()> {
+        mkerr_libc(retry(|| unsafe { libc::fsync(self.fd()) }))
+    }
+
+    pub fn datasync(&self) -> IoResult<()> {
+        return mkerr_libc(os_datasync(self.fd()));
+
+        #[cfg(any(target_os = "macos", target_os = "ios"))]
+        fn os_datasync(fd: c_int) -> c_int {
+            unsafe { libc::fcntl(fd, libc::F_FULLFSYNC) }
+        }
+        #[cfg(target_os = "linux")]
+        fn os_datasync(fd: c_int) -> c_int {
+            retry(|| unsafe { libc::fdatasync(fd) })
+        }
+        #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "linux")))]
+        fn os_datasync(fd: c_int) -> c_int {
+            retry(|| unsafe { libc::fsync(fd) })
+        }
+    }
+
+    pub fn truncate(&self, offset: i64) -> IoResult<()> {
+        mkerr_libc(retry(|| unsafe {
+            libc::ftruncate(self.fd(), offset as libc::off_t)
+        }))
+    }
+
+    pub fn fstat(&self) -> IoResult<FileStat> {
+        let mut stat: libc::stat = unsafe { mem::zeroed() };
+        match unsafe { libc::fstat(self.fd(), &mut stat) } {
+            0 => Ok(mkstat(&stat)),
+            _ => Err(super::last_error()),
+        }
+    }
+
+    /// Extract the actual filedescriptor without closing it.
+    pub fn unwrap(self) -> fd_t {
+        let fd = self.fd;
+        unsafe { mem::forget(self) };
+        fd
+    }
+}
+
+/*
+
+impl RtioTTY for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.inner_read(buf)
+    }
+    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.inner_write(buf)
+    }
+    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
+        Err(super::unimpl())
+    }
+    fn get_winsize(&mut self) -> IoResult<(int, int)> {
+        Err(super::unimpl())
+    }
+    fn isatty(&self) -> bool { false }
+}
+*/
+
+impl Drop for FileDesc {
+    fn drop(&mut self) {
+        // closing stdio file handles makes no sense, so never do it. Also, note
+        // that errors are ignored when closing a file descriptor. The reason
+        // for this is that if an error occurs we don't actually know if the
+        // file descriptor was closed or not, and if we retried (for something
+        // like EINTR), we might close another valid file descriptor (opened
+        // after we closed ours.
+        if self.close_on_drop && self.fd > libc::STDERR_FILENO {
+            let n = unsafe { libc::close(self.fd) };
+            if n != 0 {
+                println!("error {} when closing file descriptor {}", n, self.fd);
+            }
+        }
+    }
+}
+
+pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
+    let flags = match fm {
+        Open => 0,
+        Append => libc::O_APPEND,
+        Truncate => libc::O_TRUNC,
+    };
+    // Opening with a write permission must silently create the file.
+    let (flags, mode) = match fa {
+        Read => (flags | libc::O_RDONLY, 0),
+        Write => (flags | libc::O_WRONLY | libc::O_CREAT,
+                        libc::S_IRUSR | libc::S_IWUSR),
+        ReadWrite => (flags | libc::O_RDWR | libc::O_CREAT,
+                            libc::S_IRUSR | libc::S_IWUSR),
+    };
+
+    let path = path.to_c_str();
+    match retry(|| unsafe { libc::open(path.as_ptr(), flags, mode) }) {
+        -1 => Err(super::last_error()),
+        fd => Ok(FileDesc::new(fd, true)),
+    }
+}
+
+pub fn mkdir(p: &Path, mode: uint) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::mkdir(p.as_ptr(), mode as libc::mode_t) })
+}
+
+pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
+    use libc::{dirent_t};
+    use libc::{opendir, readdir_r, closedir};
+
+    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<Path> {
+        let root = unsafe { CString::new(root.as_ptr(), false) };
+        let root = Path::new(root);
+
+        dirs.into_iter().filter(|path| {
+            path.as_vec() != b"." && path.as_vec() != b".."
+        }).map(|path| root.join(path)).collect()
+    }
+
+    extern {
+        fn rust_dirent_t_size() -> libc::c_int;
+        fn rust_list_dir_val(ptr: *mut dirent_t) -> *const libc::c_char;
+    }
+
+    let size = unsafe { rust_dirent_t_size() };
+    let mut buf = Vec::<u8>::with_capacity(size as uint);
+    let ptr = buf.as_mut_slice().as_mut_ptr() as *mut dirent_t;
+
+    let p = p.to_c_str();
+    let dir_ptr = unsafe {opendir(p.as_ptr())};
+
+    if dir_ptr as uint != 0 {
+        let mut paths = vec!();
+        let mut entry_ptr = 0 as *mut dirent_t;
+        while unsafe { readdir_r(dir_ptr, ptr, &mut entry_ptr) == 0 } {
+            if entry_ptr.is_null() { break }
+            let cstr = unsafe {
+                CString::new(rust_list_dir_val(entry_ptr), false)
+            };
+            paths.push(Path::new(cstr));
+        }
+        assert_eq!(unsafe { closedir(dir_ptr) }, 0);
+        Ok(prune(&p, paths))
+    } else {
+        Err(super::last_error())
+    }
+}
+
+pub fn unlink(p: &Path) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::unlink(p.as_ptr()) })
+}
+
+pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
+    let old = old.to_c_str();
+    let new = new.to_c_str();
+    mkerr_libc(unsafe {
+        libc::rename(old.as_ptr(), new.as_ptr())
+    })
+}
+
+pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(retry(|| unsafe {
+        libc::chmod(p.as_ptr(), mode as libc::mode_t)
+    }))
+}
+
+pub fn rmdir(p: &Path) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(unsafe { libc::rmdir(p.as_ptr()) })
+}
+
+pub fn chown(p: &Path, uid: int, gid: int) -> IoResult<()> {
+    let p = p.to_c_str();
+    mkerr_libc(retry(|| unsafe {
+        libc::chown(p.as_ptr(), uid as libc::uid_t, gid as libc::gid_t)
+    }))
+}
+
+pub fn readlink(p: &Path) -> IoResult<Path> {
+    let c_path = p.to_c_str();
+    let p = c_path.as_ptr();
+    let mut len = unsafe { libc::pathconf(p as *mut _, libc::_PC_NAME_MAX) };
+    if len == -1 {
+        len = 1024; // FIXME: read PATH_MAX from C ffi?
+    }
+    let mut buf: Vec<u8> = Vec::with_capacity(len as uint);
+    match unsafe {
+        libc::readlink(p, buf.as_ptr() as *mut libc::c_char,
+                       len as libc::size_t) as libc::c_int
+    } {
+        -1 => Err(super::last_error()),
+        n => {
+            assert!(n > 0);
+            unsafe { buf.set_len(n as uint); }
+            Ok(Path::new(buf))
+        }
+    }
+}
+
+pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
+    let src = src.to_c_str();
+    let dst = dst.to_c_str();
+    mkerr_libc(unsafe { libc::symlink(src.as_ptr(), dst.as_ptr()) })
+}
+
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
+    let src = src.to_c_str();
+    let dst = dst.to_c_str();
+    mkerr_libc(unsafe { libc::link(src.as_ptr(), dst.as_ptr()) })
+}
+
+fn mkstat(stat: &libc::stat) -> FileStat {
+    // FileStat times are in milliseconds
+    fn mktime(secs: u64, nsecs: u64) -> u64 { secs * 1000 + nsecs / 1000000 }
+
+    #[cfg(not(any(target_os = "linux", target_os = "android")))]
+    fn flags(stat: &libc::stat) -> u64 { stat.st_flags as u64 }
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn flags(_stat: &libc::stat) -> u64 { 0 }
+
+    #[cfg(not(any(target_os = "linux", target_os = "android")))]
+    fn gen(stat: &libc::stat) -> u64 { stat.st_gen as u64 }
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    fn gen(_stat: &libc::stat) -> u64 { 0 }
+
+    FileStat {
+        size: stat.st_size as u64,
+        kind: match (stat.st_mode as libc::mode_t) & libc::S_IFMT {
+            libc::S_IFREG => io::TypeFile,
+            libc::S_IFDIR => io::TypeDirectory,
+            libc::S_IFIFO => io::TypeNamedPipe,
+            libc::S_IFBLK => io::TypeBlockSpecial,
+            libc::S_IFLNK => io::TypeSymlink,
+            _ => io::TypeUnknown,
+        },
+        perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
+        created: mktime(stat.st_ctime as u64, stat.st_ctime_nsec as u64),
+        modified: mktime(stat.st_mtime as u64, stat.st_mtime_nsec as u64),
+        accessed: mktime(stat.st_atime as u64, stat.st_atime_nsec as u64),
+        unstable: UnstableFileStat {
+            device: stat.st_dev as u64,
+            inode: stat.st_ino as u64,
+            rdev: stat.st_rdev as u64,
+            nlink: stat.st_nlink as u64,
+            uid: stat.st_uid as u64,
+            gid: stat.st_gid as u64,
+            blksize: stat.st_blksize as u64,
+            blocks: stat.st_blocks as u64,
+            flags: flags(stat),
+            gen: gen(stat),
+        },
+    }
+}
+
+pub fn stat(p: &Path) -> IoResult<FileStat> {
+    let p = p.to_c_str();
+    let mut stat: libc::stat = unsafe { mem::zeroed() };
+    match unsafe { libc::stat(p.as_ptr(), &mut stat) } {
+        0 => Ok(mkstat(&stat)),
+        _ => Err(super::last_error()),
+    }
+}
+
+pub fn lstat(p: &Path) -> IoResult<FileStat> {
+    let p = p.to_c_str();
+    let mut stat: libc::stat = unsafe { mem::zeroed() };
+    match unsafe { libc::lstat(p.as_ptr(), &mut stat) } {
+        0 => Ok(mkstat(&stat)),
+        _ => Err(super::last_error()),
+    }
+}
+
+pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
+    let p = p.to_c_str();
+    let buf = libc::utimbuf {
+        actime: (atime / 1000) as libc::time_t,
+        modtime: (mtime / 1000) as libc::time_t,
+    };
+    mkerr_libc(unsafe { libc::utime(p.as_ptr(), &buf) })
+}
+
+#[cfg(test)]
+mod tests {
+    use super::FileDesc;
+    use libc;
+    use os;
+    use prelude::*;
+
+    #[cfg_attr(target_os = "freebsd", ignore)] // hmm, maybe pipes have a tiny buffer
+    #[test]
+    fn test_file_desc() {
+        // Run this test with some pipes so we don't have to mess around with
+        // opening or closing files.
+        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
+        let mut reader = FileDesc::new(reader, true);
+        let mut writer = FileDesc::new(writer, true);
+
+        writer.write(b"test").ok().unwrap();
+        let mut buf = [0u8, ..4];
+        match reader.read(buf) {
+            Ok(4) => {
+                assert_eq!(buf[0], 't' as u8);
+                assert_eq!(buf[1], 'e' as u8);
+                assert_eq!(buf[2], 's' as u8);
+                assert_eq!(buf[3], 't' as u8);
+            }
+            r => panic!("invalid read: {}", r),
+        }
+
+        assert!(writer.read(buf).is_err());
+        assert!(reader.write(buf).is_err());
+    }
+}
diff --git a/src/libnative/io/file_windows.rs b/src/libstd/platform_imp/windows/fs.rs
similarity index 52%
rename from src/libnative/io/file_windows.rs
rename to src/libstd/platform_imp/windows/fs.rs
index eb4d4f22132d0..a07688b2fed03 100644
--- a/src/libnative/io/file_windows.rs
+++ b/src/libstd/platform_imp/windows/fs.rs
@@ -1,4 +1,4 @@
-// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
 // file at the top-level directory of this distribution and at
 // http://rust-lang.org/COPYRIGHT.
 //
@@ -12,42 +12,40 @@
 
 use alloc::arc::Arc;
 use libc::{mod, c_int};
-use std::c_str::CString;
-use std::mem;
-use std::os::windows::fill_utf16_buf_and_decode;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::str;
 
-pub type fd_t = libc::c_int;
+use c_str::CString;
+use mem;
+use os::windows::fill_utf16_buf_and_decode;
+use path;
+use ptr;
+use str;
+use io;
 
-struct Inner {
-    fd: fd_t,
-    close_on_drop: bool,
-}
+use prelude::*;
+use sys;
+use sys_common::{keep_going, eof, mkerr_libc};
+
+use io::{FilePermission, Write, UnstableFileStat, Open, FileAccess, FileMode};
+use io::{IoResult, IoError, FileStat, SeekStyle, Seek, Writer, Reader};
+use io::{Read, Truncate, SeekCur, SeekSet, ReadWrite, SeekEnd, Append};
+
+pub use path::WindowsPath as Path;
+pub type fd_t = libc::c_int;
 
 pub struct FileDesc {
-    inner: Arc<Inner>
+    /// The underlying C file descriptor.
+    pub fd: fd_t,
+
+    /// Whether to close the file descriptor on drop.
+    close_on_drop: bool,
 }
 
 impl FileDesc {
-    /// Create a `FileDesc` from an open C file descriptor.
-    ///
-    /// The `FileDesc` will take ownership of the specified file descriptor and
-    /// close it upon destruction if the `close_on_drop` flag is true, otherwise
-    /// it will not close the file descriptor when this `FileDesc` is dropped.
-    ///
-    /// Note that all I/O operations done on this object will be *blocking*, but
-    /// they do not require the runtime to be active.
     pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
-        FileDesc { inner: Arc::new(Inner {
-            fd: fd,
-            close_on_drop: close_on_drop
-        }) }
+        FileDesc { fd: fd, close_on_drop: close_on_drop }
     }
 
-    pub fn inner_read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&self, buf: &mut [u8]) -> IoResult<uint> {
         let mut read = 0;
         let ret = unsafe {
             libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
@@ -60,7 +58,8 @@ impl FileDesc {
             Err(super::last_error())
         }
     }
-    pub fn inner_write(&mut self, buf: &[u8]) -> IoResult<()> {
+
+    pub fn write(&self, buf: &[u8]) -> IoResult<()> {
         let mut cur = buf.as_ptr();
         let mut remaining = buf.len();
         while remaining > 0 {
@@ -80,7 +79,7 @@ impl FileDesc {
         Ok(())
     }
 
-    pub fn fd(&self) -> fd_t { self.inner.fd }
+    pub fn fd(&self) -> fd_t { self.fd }
 
     pub fn handle(&self) -> libc::HANDLE {
         unsafe { libc::get_osfhandle(self.fd()) as libc::HANDLE }
@@ -88,153 +87,67 @@ impl FileDesc {
 
     // A version of seek that takes &self so that tell can call it
     //   - the private seek should of course take &mut self.
-    fn seek_common(&self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+    fn seek_common(&self, pos: i64, style: SeekStyle) -> IoResult<u64> {
         let whence = match style {
-            rtio::SeekSet => libc::FILE_BEGIN,
-            rtio::SeekEnd => libc::FILE_END,
-            rtio::SeekCur => libc::FILE_CURRENT,
+            SeekSet => libc::FILE_BEGIN,
+            SeekEnd => libc::FILE_END,
+            SeekCur => libc::FILE_CURRENT,
         };
         unsafe {
             let mut newpos = 0;
-            match libc::SetFilePointerEx(self.handle(), pos, &mut newpos,
-                                         whence) {
+            match libc::SetFilePointerEx(self.handle(), pos, &mut newpos, whence) {
                 0 => Err(super::last_error()),
                 _ => Ok(newpos as u64),
             }
         }
     }
 
-}
-
-impl rtio::RtioFileStream for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<int> {
-        self.inner_read(buf).map(|i| i as int)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-
-    fn pread(&mut self, buf: &mut [u8], offset: u64) -> IoResult<int> {
-        let mut read = 0;
-        let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
-        overlap.Offset = offset as libc::DWORD;
-        overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
-        let ret = unsafe {
-            libc::ReadFile(self.handle(), buf.as_ptr() as libc::LPVOID,
-                           buf.len() as libc::DWORD, &mut read,
-                           &mut overlap)
-        };
-        if ret != 0 {
-            Ok(read as int)
-        } else {
-            Err(super::last_error())
-        }
-    }
-    fn pwrite(&mut self, buf: &[u8], mut offset: u64) -> IoResult<()> {
-        let mut cur = buf.as_ptr();
-        let mut remaining = buf.len();
-        let mut overlap: libc::OVERLAPPED = unsafe { mem::zeroed() };
-        while remaining > 0 {
-            overlap.Offset = offset as libc::DWORD;
-            overlap.OffsetHigh = (offset >> 32) as libc::DWORD;
-            let mut amt = 0;
-            let ret = unsafe {
-                libc::WriteFile(self.handle(), cur as libc::LPVOID,
-                                remaining as libc::DWORD, &mut amt,
-                                &mut overlap)
-            };
-            if ret != 0 {
-                remaining -= amt as uint;
-                cur = unsafe { cur.offset(amt as int) };
-                offset += amt as u64;
-            } else {
-                return Err(super::last_error())
-            }
-        }
-        Ok(())
-    }
-
-    fn seek(&mut self, pos: i64, style: rtio::SeekStyle) -> IoResult<u64> {
+    pub fn seek(&mut self, pos: i64, style: SeekStyle) -> IoResult<u64> {
         self.seek_common(pos, style)
     }
 
-    fn tell(&self) -> IoResult<u64> {
-        self.seek_common(0, rtio::SeekCur)
+    pub fn tell(&self) -> IoResult<u64> {
+        self.seek_common(0, SeekCur)
     }
 
-    fn fsync(&mut self) -> IoResult<()> {
+    pub fn fsync(&mut self) -> IoResult<()> {
         super::mkerr_winbool(unsafe {
             libc::FlushFileBuffers(self.handle())
         })
     }
 
-    fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
+    pub fn datasync(&mut self) -> IoResult<()> { return self.fsync(); }
 
-    fn truncate(&mut self, offset: i64) -> IoResult<()> {
+    pub fn truncate(&mut self, offset: i64) -> IoResult<()> {
         let orig_pos = try!(self.tell());
-        let _ = try!(self.seek(offset, rtio::SeekSet));
+        let _ = try!(self.seek(offset, SeekSet));
         let ret = unsafe {
             match libc::SetEndOfFile(self.handle()) {
                 0 => Err(super::last_error()),
                 _ => Ok(())
             }
         };
-        let _ = self.seek(orig_pos as i64, rtio::SeekSet);
+        let _ = self.seek(orig_pos as i64, SeekSet);
         return ret;
     }
 
-    fn fstat(&mut self) -> IoResult<rtio::FileStat> {
+    pub fn fstat(&mut self) -> IoResult<io::FileStat> {
         let mut stat: libc::stat = unsafe { mem::zeroed() };
         match unsafe { libc::fstat(self.fd(), &mut stat) } {
             0 => Ok(mkstat(&stat)),
             _ => Err(super::last_error()),
         }
     }
-}
 
-impl rtio::RtioPipe for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box FileDesc { inner: self.inner.clone() } as Box<rtio::RtioPipe + Send>
-    }
-
-    // Only supported on named pipes currently. Note that this doesn't have an
-    // impact on the std::io primitives, this is never called via
-    // std::io::PipeStream. If the functionality is exposed in the future, then
-    // these methods will need to be implemented.
-    fn close_read(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn close_write(&mut self) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn set_timeout(&mut self, _t: Option<u64>) {}
-    fn set_read_timeout(&mut self, _t: Option<u64>) {}
-    fn set_write_timeout(&mut self, _t: Option<u64>) {}
-}
-
-impl rtio::RtioTTY for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
+    /// Extract the actual filedescriptor without closing it.
+    pub fn unwrap(self) -> fd_t {
+        let fd = self.fd;
+        unsafe { mem::forget(self) };
+        fd
     }
-    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
-        Err(super::unimpl())
-    }
-    fn isatty(&self) -> bool { false }
 }
 
-impl Drop for Inner {
+impl Drop for FileDesc {
     fn drop(&mut self) {
         // closing stdio file handles makes no sense, so never do it. Also, note
         // that errors are ignored when closing a file descriptor. The reason
@@ -251,39 +164,26 @@ impl Drop for Inner {
     }
 }
 
-pub fn to_utf16(s: &CString) -> IoResult<Vec<u16>> {
-    match s.as_str() {
-        Some(s) => Ok({
-            let mut s = s.utf16_units().collect::<Vec<u16>>();
-            s.push(0);
-            s
-        }),
-        None => Err(IoError {
-            code: libc::ERROR_INVALID_NAME as uint,
-            extra: 0,
-            detail: Some("valid unicode input required".to_string()),
-        })
-    }
+pub fn to_utf16(s: &Path) -> IoResult<Vec<u16>> {
+    sys::to_utf16(s.as_str())
 }
 
-pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
-        -> IoResult<FileDesc> {
+pub fn open(path: &Path, fm: FileMode, fa: FileAccess) -> IoResult<FileDesc> {
     // Flags passed to open_osfhandle
     let flags = match fm {
-        rtio::Open => 0,
-        rtio::Append => libc::O_APPEND,
-        rtio::Truncate => libc::O_TRUNC,
+        Open => 0,
+        Append => libc::O_APPEND,
+        Truncate => libc::O_TRUNC,
     };
     let flags = match fa {
-        rtio::Read => flags | libc::O_RDONLY,
-        rtio::Write => flags | libc::O_WRONLY | libc::O_CREAT,
-        rtio::ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
+        Read => flags | libc::O_RDONLY,
+        Write => flags | libc::O_WRONLY | libc::O_CREAT,
+        ReadWrite => flags | libc::O_RDWR | libc::O_CREAT,
     };
-
     let mut dwDesiredAccess = match fa {
-        rtio::Read => libc::FILE_GENERIC_READ,
-        rtio::Write => libc::FILE_GENERIC_WRITE,
-        rtio::ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
+        Read => libc::FILE_GENERIC_READ,
+        Write => libc::FILE_GENERIC_WRITE,
+        ReadWrite => libc::FILE_GENERIC_READ | libc::FILE_GENERIC_WRITE
     };
 
     // libuv has a good comment about this, but the basic idea is what we try to
@@ -293,15 +193,15 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
                       libc::FILE_SHARE_DELETE;
 
     let dwCreationDisposition = match (fm, fa) {
-        (rtio::Truncate, rtio::Read) => libc::TRUNCATE_EXISTING,
-        (rtio::Truncate, _) => libc::CREATE_ALWAYS,
-        (rtio::Open, rtio::Read) => libc::OPEN_EXISTING,
-        (rtio::Open, _) => libc::OPEN_ALWAYS,
-        (rtio::Append, rtio::Read) => {
+        (Truncate, Read) => libc::TRUNCATE_EXISTING,
+        (Truncate, _) => libc::CREATE_ALWAYS,
+        (Open, Read) => libc::OPEN_EXISTING,
+        (Open, _) => libc::OPEN_ALWAYS,
+        (Append, Read) => {
             dwDesiredAccess |= libc::FILE_APPEND_DATA;
             libc::OPEN_EXISTING
         }
-        (rtio::Append, _) => {
+        (Append, _) => {
             dwDesiredAccess &= !libc::FILE_WRITE_DATA;
             dwDesiredAccess |= libc::FILE_APPEND_DATA;
             libc::OPEN_ALWAYS
@@ -337,7 +237,7 @@ pub fn open(path: &CString, fm: rtio::FileMode, fa: rtio::FileAccess)
     }
 }
 
-pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
+pub fn mkdir(p: &Path, _mode: uint) -> IoResult<()> {
     let p = try!(to_utf16(p));
     super::mkerr_winbool(unsafe {
         // FIXME: turn mode into something useful? #2623
@@ -345,20 +245,15 @@ pub fn mkdir(p: &CString, _mode: uint) -> IoResult<()> {
     })
 }
 
-pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
-    fn prune(root: &CString, dirs: Vec<Path>) -> Vec<CString> {
-        let root = unsafe { CString::new(root.as_ptr(), false) };
-        let root = Path::new(root);
-
+pub fn readdir(p: &Path) -> IoResult<Vec<Path>> {
+    fn prune(root: &Path, dirs: Vec<Path>) -> Vec<Path> {
         dirs.into_iter().filter(|path| {
             path.as_vec() != b"." && path.as_vec() != b".."
-        }).map(|path| root.join(path).to_c_str()).collect()
+        }).map(|path| root.join(path)).collect()
     }
 
-    let star = Path::new(unsafe {
-        CString::new(p.as_ptr(), false)
-    }).join("*");
-    let path = try!(to_utf16(&star.to_c_str()));
+    let star = p.join("*");
+    let path = try!(to_utf16(&star));
 
     unsafe {
         let mut wfd = mem::zeroed();
@@ -374,8 +269,8 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
                         None => {
                             assert!(libc::FindClose(find_handle) != 0);
                             return Err(IoError {
-                                code: super::c::ERROR_ILLEGAL_CHARACTER as uint,
-                                extra: 0,
+                                kind: io::InvalidInput,
+                                desc: "path was not valid UTF-16",
                                 detail: Some(format!("path was not valid UTF-16: {}", filename)),
                             })
                         }, // FIXME #12056: Convert the UCS-2 to invalid utf-8 instead of erroring
@@ -391,42 +286,74 @@ pub fn readdir(p: &CString) -> IoResult<Vec<CString>> {
     }
 }
 
-pub fn unlink(p: &CString) -> IoResult<()> {
-    let p = try!(to_utf16(p));
-    super::mkerr_winbool(unsafe {
-        libc::DeleteFileW(p.as_ptr())
-    })
+pub fn unlink(p: &Path) -> IoResult<()> {
+    fn do_unlink(p_utf16: &Vec<u16>) -> IoResult<()> {
+        super::mkerr_winbool(unsafe { libc::DeleteFileW(p_utf16.as_ptr()) })
+    }
+
+    let p_utf16 = try!(to_utf16(p));
+    let res = do_unlink(&p_utf16);
+    match res {
+        Ok(()) => Ok(()),
+        Err(e) => {
+            // FIXME: change the code below to use more direct calls
+            // than `stat` and `chmod`, to avoid re-conversion to
+            // utf16 etc.
+
+            // On unix, a readonly file can be successfully removed. On windows,
+            // however, it cannot. To keep the two platforms in line with
+            // respect to their behavior, catch this case on windows, attempt to
+            // change it to read-write, and then remove the file.
+            if e.kind == io::PermissionDenied {
+                let stat = match stat(p) {
+                    Ok(stat) => stat,
+                    Err(..) => return Err(e),
+                };
+                if stat.perm.intersects(io::USER_WRITE) { return Err(e) }
+
+                match chmod(p, (stat.perm | io::USER_WRITE).bits() as uint) {
+                    Ok(()) => do_unlink(&p_utf16),
+                    Err(..) => {
+                        // Try to put it back as we found it
+                        let _ = chmod(p, stat.perm.bits() as uint);
+                        Err(e)
+                    }
+                }
+            } else {
+                Err(e)
+            }
+        }
+    }
 }
 
-pub fn rename(old: &CString, new: &CString) -> IoResult<()> {
+pub fn rename(old: &Path, new: &Path) -> IoResult<()> {
     let old = try!(to_utf16(old));
     let new = try!(to_utf16(new));
     super::mkerr_winbool(unsafe {
-        libc::MoveFileExW(old.as_ptr(), new.as_ptr(),
-                          libc::MOVEFILE_REPLACE_EXISTING)
+        libc::MoveFileExW(old.as_ptr(), new.as_ptr(), libc::MOVEFILE_REPLACE_EXISTING)
     })
 }
 
-pub fn chmod(p: &CString, mode: uint) -> IoResult<()> {
+pub fn chmod(p: &Path, mode: uint) -> IoResult<()> {
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe {
+    mkerr_libc(unsafe {
         libc::wchmod(p.as_ptr(), mode as libc::c_int)
     })
 }
 
-pub fn rmdir(p: &CString) -> IoResult<()> {
+pub fn rmdir(p: &Path) -> IoResult<()> {
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
+    mkerr_libc(unsafe { libc::wrmdir(p.as_ptr()) })
 }
 
-pub fn chown(_p: &CString, _uid: int, _gid: int) -> IoResult<()> {
+pub fn chown(_p: &Path, _uid: int, _gid: int) -> IoResult<()> {
     // libuv has this as a no-op, so seems like this should as well?
     Ok(())
 }
 
-pub fn readlink(p: &CString) -> IoResult<CString> {
+pub fn readlink(p: &Path) -> IoResult<Path> {
     // FIXME: I have a feeling that this reads intermediate symlinks as well.
-    use io::c::compat::kernel32::GetFinalPathNameByHandleW;
+    use sys::c::compat::kernel32::GetFinalPathNameByHandleW;
     let p = try!(to_utf16(p));
     let handle = unsafe {
         libc::CreateFileW(p.as_ptr(),
@@ -449,18 +376,18 @@ pub fn readlink(p: &CString) -> IoResult<CString> {
                                   libc::VOLUME_NAME_DOS)
     });
     let ret = match ret {
-        Some(ref s) if s.as_slice().starts_with(r"\\?\") => {
-            Ok(Path::new(s.as_slice().slice_from(4)).to_c_str())
+        Some(ref s) if s.as_slice().starts_with(r"\\?\") => { // "
+            Ok(Path::new(s.as_slice().slice_from(4)))
         }
-        Some(s) => Ok(Path::new(s).to_c_str()),
+        Some(s) => Ok(Path::new(s)),
         None => Err(super::last_error()),
     };
     assert!(unsafe { libc::CloseHandle(handle) } != 0);
     return ret;
 }
 
-pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
-    use io::c::compat::kernel32::CreateSymbolicLinkW;
+pub fn symlink(src: &Path, dst: &Path) -> IoResult<()> {
+    use sys::c::compat::kernel32::CreateSymbolicLinkW;
     let src = try!(to_utf16(src));
     let dst = try!(to_utf16(dst));
     super::mkerr_winbool(unsafe {
@@ -468,7 +395,7 @@ pub fn symlink(src: &CString, dst: &CString) -> IoResult<()> {
     })
 }
 
-pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
+pub fn link(src: &Path, dst: &Path) -> IoResult<()> {
     let src = try!(to_utf16(src));
     let dst = try!(to_utf16(dst));
     super::mkerr_winbool(unsafe {
@@ -476,28 +403,37 @@ pub fn link(src: &CString, dst: &CString) -> IoResult<()> {
     })
 }
 
-fn mkstat(stat: &libc::stat) -> rtio::FileStat {
-    rtio::FileStat {
+fn mkstat(stat: &libc::stat) -> FileStat {
+    FileStat {
         size: stat.st_size as u64,
-        kind: stat.st_mode as u64,
-        perm: stat.st_mode as u64,
+        kind: match (stat.st_mode as libc::c_int) & libc::S_IFMT {
+            libc::S_IFREG => io::TypeFile,
+            libc::S_IFDIR => io::TypeDirectory,
+            libc::S_IFIFO => io::TypeNamedPipe,
+            libc::S_IFBLK => io::TypeBlockSpecial,
+            libc::S_IFLNK => io::TypeSymlink,
+            _ => io::TypeUnknown,
+        },
+        perm: FilePermission::from_bits_truncate(stat.st_mode as u32),
         created: stat.st_ctime as u64,
         modified: stat.st_mtime as u64,
         accessed: stat.st_atime as u64,
-        device: stat.st_dev as u64,
-        inode: stat.st_ino as u64,
-        rdev: stat.st_rdev as u64,
-        nlink: stat.st_nlink as u64,
-        uid: stat.st_uid as u64,
-        gid: stat.st_gid as u64,
-        blksize: 0,
-        blocks: 0,
-        flags: 0,
-        gen: 0,
+        unstable: UnstableFileStat {
+            device: stat.st_dev as u64,
+            inode: stat.st_ino as u64,
+            rdev: stat.st_rdev as u64,
+            nlink: stat.st_nlink as u64,
+            uid: stat.st_uid as u64,
+            gid: stat.st_gid as u64,
+            blksize:0,
+            blocks: 0,
+            flags: 0,
+            gen: 0,
+        },
     }
 }
 
-pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
+pub fn stat(p: &Path) -> IoResult<FileStat> {
     let mut stat: libc::stat = unsafe { mem::zeroed() };
     let p = try!(to_utf16(p));
     match unsafe { libc::wstat(p.as_ptr(), &mut stat) } {
@@ -506,18 +442,19 @@ pub fn stat(p: &CString) -> IoResult<rtio::FileStat> {
     }
 }
 
-pub fn lstat(_p: &CString) -> IoResult<rtio::FileStat> {
+// FIXME: move this to platform-specific modules (for now)?
+pub fn lstat(_p: &Path) -> IoResult<FileStat> {
     // FIXME: implementation is missing
     Err(super::unimpl())
 }
 
-pub fn utime(p: &CString, atime: u64, mtime: u64) -> IoResult<()> {
+pub fn utime(p: &Path, atime: u64, mtime: u64) -> IoResult<()> {
     let mut buf = libc::utimbuf {
         actime: atime as libc::time64_t,
         modtime: mtime as libc::time64_t,
     };
     let p = try!(to_utf16(p));
-    super::mkerr_libc(unsafe {
+    mkerr_libc(unsafe {
         libc::wutime(p.as_ptr(), &mut buf)
     })
 }

From d34b1b0ca9bf5e0d7cd30952f5de0ab09ed57b41 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Fri, 10 Oct 2014 10:11:49 -0700
Subject: [PATCH 04/11] Runtime removal: refactor pipes and networking

This patch continues the runtime removal by moving pipe and
networking-related code into `sys`.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/mod.rs                       |   54 -
 src/librustrt/rtio.rs                         |  127 --
 src/libstd/io/net/addrinfo.rs                 |   32 +-
 src/libstd/io/net/mod.rs                      |   45 +-
 src/libstd/io/net/pipe.rs                     |   65 +-
 src/libstd/io/net/tcp.rs                      |  105 +-
 src/libstd/io/net/udp.rs                      |   60 +-
 src/libstd/io/pipe.rs                         |   58 +-
 src/libstd/os.rs                              |   35 +-
 .../io => libstd/sys/common}/net.rs           | 1110 +++++++----------
 src/libstd/sys/unix/mod.rs                    |   50 +-
 src/libstd/sys/unix/os.rs                     |   11 +
 .../pipe_unix.rs => libstd/sys/unix/pipe.rs}  |  146 +--
 src/libstd/sys/unix/tcp.rs                    |  157 +++
 src/libstd/sys/unix/udp.rs                    |   11 +
 src/libstd/sys/windows/mod.rs                 |   12 +-
 .../sys/windows/pipe.rs}                      |  164 ++-
 src/libstd/sys/windows/tcp.rs                 |  219 ++++
 src/libstd/sys/windows/udp.rs                 |   11 +
 19 files changed, 1183 insertions(+), 1289 deletions(-)
 rename src/{libnative/io => libstd/sys/common}/net.rs (53%)
 rename src/{libnative/io/pipe_unix.rs => libstd/sys/unix/pipe.rs} (67%)
 create mode 100644 src/libstd/sys/unix/tcp.rs
 create mode 100644 src/libstd/sys/unix/udp.rs
 rename src/{libnative/io/pipe_windows.rs => libstd/sys/windows/pipe.rs} (89%)
 create mode 100644 src/libstd/sys/windows/tcp.rs
 create mode 100644 src/libstd/sys/windows/udp.rs

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index baf58b83dcd25..2a76bc29f7c37 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -35,8 +35,6 @@ pub use self::process::Process;
 mod helper_thread;
 
 // Native I/O implementations
-pub mod addrinfo;
-pub mod net;
 pub mod process;
 mod util;
 
@@ -53,14 +51,6 @@ pub mod timer;
 #[path = "timer_windows.rs"]
 pub mod timer;
 
-#[cfg(unix)]
-#[path = "pipe_unix.rs"]
-pub mod pipe;
-
-#[cfg(windows)]
-#[path = "pipe_windows.rs"]
-pub mod pipe;
-
 #[cfg(windows)]
 #[path = "tty_windows.rs"]
 mod tty;
@@ -126,52 +116,11 @@ pub struct IoFactory {
 
 impl IoFactory {
     pub fn new() -> IoFactory {
-        net::init();
         IoFactory { _cannot_construct_outside_of_this_module: () }
     }
 }
 
 impl rtio::IoFactory for IoFactory {
-    // networking
-    fn tcp_connect(&mut self, addr: rtio::SocketAddr,
-                   timeout: Option<u64>)
-        -> IoResult<Box<rtio::RtioTcpStream + Send>>
-    {
-        net::TcpStream::connect(addr, timeout).map(|s| {
-            box s as Box<rtio::RtioTcpStream + Send>
-        })
-    }
-    fn tcp_bind(&mut self, addr: rtio::SocketAddr)
-                -> IoResult<Box<rtio::RtioTcpListener + Send>> {
-        net::TcpListener::bind(addr).map(|s| {
-            box s as Box<rtio::RtioTcpListener + Send>
-        })
-    }
-    fn udp_bind(&mut self, addr: rtio::SocketAddr)
-                -> IoResult<Box<rtio::RtioUdpSocket + Send>> {
-        net::UdpSocket::bind(addr).map(|u| {
-            box u as Box<rtio::RtioUdpSocket + Send>
-        })
-    }
-    fn unix_bind(&mut self, path: &CString)
-                 -> IoResult<Box<rtio::RtioUnixListener + Send>> {
-        pipe::UnixListener::bind(path).map(|s| {
-            box s as Box<rtio::RtioUnixListener + Send>
-        })
-    }
-    fn unix_connect(&mut self, path: &CString,
-                    timeout: Option<u64>) -> IoResult<Box<rtio::RtioPipe + Send>> {
-        pipe::UnixStream::connect(path, timeout).map(|s| {
-            box s as Box<rtio::RtioPipe + Send>
-        })
-    }
-    fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
-                          hint: Option<rtio::AddrinfoHint>)
-        -> IoResult<Vec<rtio::AddrinfoInfo>>
-    {
-        addrinfo::GetAddrInfoRequest::run(host, servname, hint)
-    }
-
     // misc
     fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
         timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
@@ -189,9 +138,6 @@ impl rtio::IoFactory for IoFactory {
     fn kill(&mut self, pid: libc::pid_t, signum: int) -> IoResult<()> {
         process::Process::kill(pid, signum)
     }
-    fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<rtio::RtioPipe + Send>> {
-        Ok(box file::FileDesc::new(fd, true) as Box<rtio::RtioPipe + Send>)
-    }
     #[cfg(unix)]
     fn tty_open(&mut self, fd: c_int, _readable: bool)
                 -> IoResult<Box<rtio::RtioTTY + Send>> {
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index 1f3ef60e6fb2b..3ebfcaea687f1 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -13,13 +13,9 @@
 use core::prelude::*;
 use alloc::boxed::Box;
 use collections::string::String;
-use collections::vec::Vec;
-use core::fmt;
 use core::mem;
 use libc::c_int;
-use libc;
 
-use c_str::CString;
 use local::Local;
 use task::Task;
 
@@ -173,87 +169,15 @@ impl<'a> LocalIo<'a> {
 }
 
 pub trait IoFactory {
-    // networking
-    fn tcp_connect(&mut self, addr: SocketAddr,
-                   timeout: Option<u64>) -> IoResult<Box<RtioTcpStream + Send>>;
-    fn tcp_bind(&mut self, addr: SocketAddr)
-                -> IoResult<Box<RtioTcpListener + Send>>;
-    fn udp_bind(&mut self, addr: SocketAddr)
-                -> IoResult<Box<RtioUdpSocket + Send>>;
-    fn unix_bind(&mut self, path: &CString)
-                 -> IoResult<Box<RtioUnixListener + Send>>;
-    fn unix_connect(&mut self, path: &CString,
-                    timeout: Option<u64>) -> IoResult<Box<RtioPipe + Send>>;
-    fn get_host_addresses(&mut self, host: Option<&str>, servname: Option<&str>,
-                          hint: Option<AddrinfoHint>)
-                          -> IoResult<Vec<AddrinfoInfo>>;
-
-    // misc
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
     fn spawn(&mut self, cfg: ProcessConfig)
             -> IoResult<(Box<RtioProcess + Send>,
                          Vec<Option<Box<RtioPipe + Send>>>)>;
     fn kill(&mut self, pid: libc::pid_t, signal: int) -> IoResult<()>;
-    fn pipe_open(&mut self, fd: c_int) -> IoResult<Box<RtioPipe + Send>>;
     fn tty_open(&mut self, fd: c_int, readable: bool)
             -> IoResult<Box<RtioTTY + Send>>;
 }
 
-pub trait RtioTcpListener : RtioSocket {
-    fn listen(self: Box<Self>) -> IoResult<Box<RtioTcpAcceptor + Send>>;
-}
-
-pub trait RtioTcpAcceptor : RtioSocket {
-    fn accept(&mut self) -> IoResult<Box<RtioTcpStream + Send>>;
-    fn accept_simultaneously(&mut self) -> IoResult<()>;
-    fn dont_accept_simultaneously(&mut self) -> IoResult<()>;
-    fn set_timeout(&mut self, timeout: Option<u64>);
-    fn clone(&self) -> Box<RtioTcpAcceptor + Send>;
-    fn close_accept(&mut self) -> IoResult<()>;
-}
-
-pub trait RtioTcpStream : RtioSocket {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
-    fn write(&mut self, buf: &[u8]) -> IoResult<()>;
-    fn peer_name(&mut self) -> IoResult<SocketAddr>;
-    fn control_congestion(&mut self) -> IoResult<()>;
-    fn nodelay(&mut self) -> IoResult<()>;
-    fn keepalive(&mut self, delay_in_seconds: uint) -> IoResult<()>;
-    fn letdie(&mut self) -> IoResult<()>;
-    fn clone(&self) -> Box<RtioTcpStream + Send>;
-    fn close_write(&mut self) -> IoResult<()>;
-    fn close_read(&mut self) -> IoResult<()>;
-    fn set_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
-}
-
-pub trait RtioSocket {
-    fn socket_name(&mut self) -> IoResult<SocketAddr>;
-}
-
-pub trait RtioUdpSocket : RtioSocket {
-    fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)>;
-    fn send_to(&mut self, buf: &[u8], dst: SocketAddr) -> IoResult<()>;
-
-    fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()>;
-    fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()>;
-
-    fn loop_multicast_locally(&mut self) -> IoResult<()>;
-    fn dont_loop_multicast_locally(&mut self) -> IoResult<()>;
-
-    fn multicast_time_to_live(&mut self, ttl: int) -> IoResult<()>;
-    fn time_to_live(&mut self, ttl: int) -> IoResult<()>;
-
-    fn hear_broadcasts(&mut self) -> IoResult<()>;
-    fn ignore_broadcasts(&mut self) -> IoResult<()>;
-
-    fn clone(&self) -> Box<RtioUdpSocket + Send>;
-    fn set_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
-}
-
 pub trait RtioTimer {
     fn sleep(&mut self, msecs: u64);
     fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>);
@@ -313,54 +237,3 @@ pub struct IoError {
 }
 
 pub type IoResult<T> = Result<T, IoError>;
-
-#[deriving(PartialEq, Eq)]
-pub enum IpAddr {
-    Ipv4Addr(u8, u8, u8, u8),
-    Ipv6Addr(u16, u16, u16, u16, u16, u16, u16, u16),
-}
-
-impl fmt::Show for IpAddr {
-    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
-        match *self {
-            Ipv4Addr(a, b, c, d) => write!(fmt, "{}.{}.{}.{}", a, b, c, d),
-            Ipv6Addr(a, b, c, d, e, f, g, h) => {
-                write!(fmt,
-                       "{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}:{:04x}",
-                       a, b, c, d, e, f, g, h)
-            }
-        }
-    }
-}
-
-#[deriving(PartialEq, Eq)]
-pub struct SocketAddr {
-    pub ip: IpAddr,
-    pub port: u16,
-}
-
-pub enum StdioContainer {
-    Ignored,
-    InheritFd(i32),
-    CreatePipe(bool, bool),
-}
-
-pub enum ProcessExit {
-    ExitStatus(int),
-    ExitSignal(int),
-}
-
-pub struct AddrinfoHint {
-    pub family: uint,
-    pub socktype: uint,
-    pub protocol: uint,
-    pub flags: uint,
-}
-
-pub struct AddrinfoInfo {
-    pub address: SocketAddr,
-    pub family: uint,
-    pub socktype: uint,
-    pub protocol: uint,
-    pub flags: uint,
-}
diff --git a/src/libstd/io/net/addrinfo.rs b/src/libstd/io/net/addrinfo.rs
index 3c72f58b10d8f..22775d54eff1b 100644
--- a/src/libstd/io/net/addrinfo.rs
+++ b/src/libstd/io/net/addrinfo.rs
@@ -20,12 +20,10 @@ getaddrinfo()
 #![allow(missing_docs)]
 
 use iter::Iterator;
-use io::{IoResult, IoError};
+use io::{IoResult};
 use io::net::ip::{SocketAddr, IpAddr};
 use option::{Option, Some, None};
-use result::{Ok, Err};
-use rt::rtio::{IoFactory, LocalIo};
-use rt::rtio;
+use sys;
 use vec::Vec;
 
 /// Hints to the types of sockets that are desired when looking up hosts
@@ -94,31 +92,7 @@ pub fn get_host_addresses(host: &str) -> IoResult<Vec<IpAddr>> {
 #[allow(unused_variables)]
 fn lookup(hostname: Option<&str>, servname: Option<&str>, hint: Option<Hint>)
           -> IoResult<Vec<Info>> {
-    let hint = hint.map(|Hint { family, socktype, protocol, flags }| {
-        rtio::AddrinfoHint {
-            family: family,
-            socktype: 0, // FIXME: this should use the above variable
-            protocol: 0, // FIXME: this should use the above variable
-            flags: flags,
-        }
-    });
-    match LocalIo::maybe_raise(|io| {
-        io.get_host_addresses(hostname, servname, hint)
-    }) {
-        Ok(v) => Ok(v.into_iter().map(|info| {
-            Info {
-                address: SocketAddr {
-                    ip: super::from_rtio(info.address.ip),
-                    port: info.address.port,
-                },
-                family: info.family,
-                socktype: None, // FIXME: this should use the above variable
-                protocol: None, // FIXME: this should use the above variable
-                flags: info.flags,
-            }
-        }).collect()),
-        Err(e) => Err(IoError::from_rtio_error(e)),
-    }
+    sys::addrinfo::get_host_addresses(hostname, servname, hint)
 }
 
 // Ignored on android since we cannot give tcp/ip
diff --git a/src/libstd/io/net/mod.rs b/src/libstd/io/net/mod.rs
index b9b50a55a10f2..5b1747876d7e0 100644
--- a/src/libstd/io/net/mod.rs
+++ b/src/libstd/io/net/mod.rs
@@ -12,9 +12,8 @@
 
 use io::{IoError, IoResult, InvalidInput};
 use option::None;
-use result::{Result, Ok, Err};
-use rt::rtio;
-use self::ip::{Ipv4Addr, Ipv6Addr, IpAddr, SocketAddr, ToSocketAddr};
+use result::{Ok, Err};
+use self::ip::{SocketAddr, ToSocketAddr};
 
 pub use self::addrinfo::get_host_addresses;
 
@@ -24,46 +23,6 @@ pub mod udp;
 pub mod ip;
 pub mod pipe;
 
-fn to_rtio(ip: IpAddr) -> rtio::IpAddr {
-    match ip {
-        Ipv4Addr(a, b, c, d) => rtio::Ipv4Addr(a, b, c, d),
-        Ipv6Addr(a, b, c, d, e, f, g, h) => {
-            rtio::Ipv6Addr(a, b, c, d, e, f, g, h)
-        }
-    }
-}
-
-fn from_rtio(ip: rtio::IpAddr) -> IpAddr {
-    match ip {
-        rtio::Ipv4Addr(a, b, c, d) => Ipv4Addr(a, b, c, d),
-        rtio::Ipv6Addr(a, b, c, d, e, f, g, h) => {
-            Ipv6Addr(a, b, c, d, e, f, g, h)
-        }
-    }
-}
-
-fn with_addresses_io<A: ToSocketAddr, T>(
-    addr: A,
-    action: |&mut rtio::IoFactory, rtio::SocketAddr| -> Result<T, rtio::IoError>
-) -> Result<T, IoError> {
-    const DEFAULT_ERROR: IoError = IoError {
-        kind: InvalidInput,
-        desc: "no addresses found for hostname",
-        detail: None
-    };
-
-    let addresses = try!(addr.to_socket_addr_all());
-    let mut err = DEFAULT_ERROR;
-    for addr in addresses.into_iter() {
-        let addr = rtio::SocketAddr { ip: to_rtio(addr.ip), port: addr.port };
-        match rtio::LocalIo::maybe_raise(|io| action(io, addr)) {
-            Ok(r) => return Ok(r),
-            Err(e) => err = IoError::from_rtio_error(e)
-        }
-    }
-    Err(err)
-}
-
 fn with_addresses<A: ToSocketAddr, T>(addr: A, action: |SocketAddr| -> IoResult<T>)
     -> IoResult<T> {
     const DEFAULT_ERROR: IoError = IoError {
diff --git a/src/libstd/io/net/pipe.rs b/src/libstd/io/net/pipe.rs
index 8c7deadebea10..111b0f2b081fc 100644
--- a/src/libstd/io/net/pipe.rs
+++ b/src/libstd/io/net/pipe.rs
@@ -26,17 +26,20 @@ instances as clients.
 
 use prelude::*;
 
-use io::{Listener, Acceptor, IoResult, IoError, TimedOut, standard_error};
-use rt::rtio::{IoFactory, LocalIo, RtioUnixListener};
-use rt::rtio::{RtioUnixAcceptor, RtioPipe};
+use io::{Listener, Acceptor, IoResult, TimedOut, standard_error};
 use time::Duration;
 
+use sys::pipe::UnixStream as UnixStreamImp;
+use sys::pipe::UnixListener as UnixListenerImp;
+use sys::pipe::UnixAcceptor as UnixAcceptorImp;
+
 /// A stream which communicates over a named pipe.
 pub struct UnixStream {
-    obj: Box<RtioPipe + Send>,
+    inner: UnixStreamImp,
 }
 
 impl UnixStream {
+
     /// Connect to a pipe named by `path`. This will attempt to open a
     /// connection to the underlying socket.
     ///
@@ -53,9 +56,8 @@ impl UnixStream {
     /// stream.write([1, 2, 3]);
     /// ```
     pub fn connect<P: ToCStr>(path: &P) -> IoResult<UnixStream> {
-        LocalIo::maybe_raise(|io| {
-            io.unix_connect(&path.to_c_str(), None).map(|p| UnixStream { obj: p })
-        }).map_err(IoError::from_rtio_error)
+        UnixStreamImp::connect(&path.to_c_str(), None)
+            .map(|inner| UnixStream { inner: inner })
     }
 
     /// Connect to a pipe named by `path`, timing out if the specified number of
@@ -73,10 +75,8 @@ impl UnixStream {
             return Err(standard_error(TimedOut));
         }
 
-        LocalIo::maybe_raise(|io| {
-            let s = io.unix_connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64));
-            s.map(|p| UnixStream { obj: p })
-        }).map_err(IoError::from_rtio_error)
+        UnixStreamImp::connect(&path.to_c_str(), Some(timeout.num_milliseconds() as u64))
+            .map(|inner| UnixStream { inner: inner })
     }
 
 
@@ -88,7 +88,7 @@ impl UnixStream {
     /// Note that this method affects all cloned handles associated with this
     /// stream, not just this one handle.
     pub fn close_read(&mut self) -> IoResult<()> {
-        self.obj.close_read().map_err(IoError::from_rtio_error)
+        self.inner.close_read()
     }
 
     /// Closes the writing half of this connection.
@@ -99,7 +99,7 @@ impl UnixStream {
     /// Note that this method affects all cloned handles associated with this
     /// stream, not just this one handle.
     pub fn close_write(&mut self) -> IoResult<()> {
-        self.obj.close_write().map_err(IoError::from_rtio_error)
+        self.inner.close_write()
     }
 
     /// Sets the read/write timeout for this socket.
@@ -107,7 +107,7 @@ impl UnixStream {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_timeout(timeout_ms)
+        self.inner.set_timeout(timeout_ms)
     }
 
     /// Sets the read timeout for this socket.
@@ -115,7 +115,7 @@ impl UnixStream {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_read_timeout(timeout_ms)
+        self.inner.set_read_timeout(timeout_ms)
     }
 
     /// Sets the write timeout for this socket.
@@ -123,36 +123,35 @@ impl UnixStream {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_write_timeout(timeout_ms)
+        self.inner.set_write_timeout(timeout_ms)
     }
 }
 
 impl Clone for UnixStream {
     fn clone(&self) -> UnixStream {
-        UnixStream { obj: self.obj.clone() }
+        UnixStream { inner: self.inner.clone() }
     }
 }
 
 impl Reader for UnixStream {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.obj.read(buf).map_err(IoError::from_rtio_error)
+        self.inner.read(buf)
     }
 }
 
 impl Writer for UnixStream {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.obj.write(buf).map_err(IoError::from_rtio_error)
+        self.inner.write(buf)
     }
 }
 
 /// A value that can listen for incoming named pipe connection requests.
 pub struct UnixListener {
     /// The internal, opaque runtime Unix listener.
-    obj: Box<RtioUnixListener + Send>,
+    inner: UnixListenerImp,
 }
 
 impl UnixListener {
-
     /// Creates a new listener, ready to receive incoming connections on the
     /// specified socket. The server will be named by `path`.
     ///
@@ -175,24 +174,22 @@ impl UnixListener {
     /// # }
     /// ```
     pub fn bind<P: ToCStr>(path: &P) -> IoResult<UnixListener> {
-        LocalIo::maybe_raise(|io| {
-            io.unix_bind(&path.to_c_str()).map(|s| UnixListener { obj: s })
-        }).map_err(IoError::from_rtio_error)
+        UnixListenerImp::bind(&path.to_c_str())
+            .map(|inner| UnixListener { inner: inner })
     }
 }
 
 impl Listener<UnixStream, UnixAcceptor> for UnixListener {
     fn listen(self) -> IoResult<UnixAcceptor> {
-        self.obj.listen().map(|obj| {
-            UnixAcceptor { obj: obj }
-        }).map_err(IoError::from_rtio_error)
+        self.inner.listen()
+            .map(|inner| UnixAcceptor { inner: inner })
     }
 }
 
 /// A value that can accept named pipe connections, returned from `listen()`.
 pub struct UnixAcceptor {
     /// The internal, opaque runtime Unix acceptor.
-    obj: Box<RtioUnixAcceptor + Send>,
+    inner: UnixAcceptorImp
 }
 
 impl UnixAcceptor {
@@ -210,7 +207,7 @@ impl UnixAcceptor {
     #[experimental = "the name and arguments to this function are likely \
                       to change"]
     pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_timeout(timeout_ms)
+        self.inner.set_timeout(timeout_ms)
     }
 
     /// Closes the accepting capabilities of this acceptor.
@@ -219,15 +216,15 @@ impl UnixAcceptor {
     /// more information can be found in that documentation.
     #[experimental]
     pub fn close_accept(&mut self) -> IoResult<()> {
-        self.obj.close_accept().map_err(IoError::from_rtio_error)
+        self.inner.close_accept()
     }
 }
 
 impl Acceptor<UnixStream> for UnixAcceptor {
     fn accept(&mut self) -> IoResult<UnixStream> {
-        self.obj.accept().map(|s| {
-            UnixStream { obj: s }
-        }).map_err(IoError::from_rtio_error)
+        self.inner.accept().map(|s| {
+            UnixStream { inner: s }
+        })
     }
 }
 
@@ -246,7 +243,7 @@ impl Clone for UnixAcceptor {
     /// This function is useful for creating a handle to invoke `close_accept`
     /// on to wake up any other task blocked in `accept`.
     fn clone(&self) -> UnixAcceptor {
-        UnixAcceptor { obj: self.obj.clone() }
+        UnixAcceptor { inner: self.inner.clone() }
     }
 }
 
diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs
index 928c858673963..2545e07cbb5c2 100644
--- a/src/libstd/io/net/tcp.rs
+++ b/src/libstd/io/net/tcp.rs
@@ -20,19 +20,17 @@
 use clone::Clone;
 use io::IoResult;
 use iter::Iterator;
-use result::{Ok,Err};
+use result::Err;
 use io::net::ip::{SocketAddr, ToSocketAddr};
-use io::IoError;
 use io::{Reader, Writer, Listener, Acceptor};
 use io::{standard_error, TimedOut};
-use kinds::Send;
 use option::{None, Some, Option};
-use boxed::Box;
-use rt::rtio::{IoFactory, RtioSocket, RtioTcpListener};
-use rt::rtio::{RtioTcpAcceptor, RtioTcpStream};
-use rt::rtio;
 use time::Duration;
 
+use sys::tcp::TcpStream as TcpStreamImp;
+use sys::tcp::TcpListener as TcpListenerImp;
+use sys::tcp::TcpAcceptor as TcpAcceptorImp;
+
 /// A structure which represents a TCP stream between a local socket and a
 /// remote socket.
 ///
@@ -50,12 +48,12 @@ use time::Duration;
 /// drop(stream); // close the connection
 /// ```
 pub struct TcpStream {
-    obj: Box<RtioTcpStream + Send>,
+    inner: TcpStreamImp,
 }
 
 impl TcpStream {
-    fn new(s: Box<RtioTcpStream + Send>) -> TcpStream {
-        TcpStream { obj: s }
+    fn new(s: TcpStreamImp) -> TcpStream {
+        TcpStream { inner: s }
     }
 
     /// Open a TCP connection to a remote host.
@@ -64,7 +62,9 @@ impl TcpStream {
     /// trait can be supplied for the address; see this trait documentation for
     /// concrete examples.
     pub fn connect<A: ToSocketAddr>(addr: A) -> IoResult<TcpStream> {
-        super::with_addresses_io(addr, |io, addr| io.tcp_connect(addr, None).map(TcpStream::new))
+        super::with_addresses(addr, |addr| {
+            TcpStreamImp::connect(addr, None).map(TcpStream::new)
+        })
     }
 
     /// Creates a TCP connection to a remote socket address, timing out after
@@ -86,39 +86,26 @@ impl TcpStream {
             return Err(standard_error(TimedOut));
         }
 
-        super::with_addresses_io(addr, |io, addr|
-            io.tcp_connect(addr, Some(timeout.num_milliseconds() as u64)).map(TcpStream::new)
-        )
+        super::with_addresses(addr, |addr| {
+            TcpStreamImp::connect(addr, Some(timeout.num_milliseconds() as u64))
+                .map(TcpStream::new)
+        })
     }
 
     /// Returns the socket address of the remote peer of this TCP connection.
     pub fn peer_name(&mut self) -> IoResult<SocketAddr> {
-        match self.obj.peer_name() {
-            Ok(rtio::SocketAddr { ip, port }) => {
-                Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
-            }
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.peer_name()
     }
 
     /// Returns the socket address of the local half of this TCP connection.
     pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
-        match self.obj.socket_name() {
-            Ok(rtio::SocketAddr { ip, port }) => {
-                Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
-            }
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.socket_name()
     }
 
     /// Sets the nodelay flag on this connection to the boolean specified
     #[experimental]
     pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
-        if nodelay {
-            self.obj.nodelay()
-        } else {
-            self.obj.control_congestion()
-        }.map_err(IoError::from_rtio_error)
+        self.inner.set_nodelay(nodelay)
     }
 
     /// Sets the keepalive timeout to the timeout specified.
@@ -128,10 +115,7 @@ impl TcpStream {
     /// specified time, in seconds.
     #[experimental]
     pub fn set_keepalive(&mut self, delay_in_seconds: Option<uint>) -> IoResult<()> {
-        match delay_in_seconds {
-            Some(i) => self.obj.keepalive(i),
-            None => self.obj.letdie(),
-        }.map_err(IoError::from_rtio_error)
+        self.inner.set_keepalive(delay_in_seconds)
     }
 
     /// Closes the reading half of this connection.
@@ -165,7 +149,7 @@ impl TcpStream {
     /// Note that this method affects all cloned handles associated with this
     /// stream, not just this one handle.
     pub fn close_read(&mut self) -> IoResult<()> {
-        self.obj.close_read().map_err(IoError::from_rtio_error)
+        self.inner.close_read()
     }
 
     /// Closes the writing half of this connection.
@@ -176,7 +160,7 @@ impl TcpStream {
     /// Note that this method affects all cloned handles associated with this
     /// stream, not just this one handle.
     pub fn close_write(&mut self) -> IoResult<()> {
-        self.obj.close_write().map_err(IoError::from_rtio_error)
+        self.inner.close_write()
     }
 
     /// Sets a timeout, in milliseconds, for blocking operations on this stream.
@@ -198,7 +182,7 @@ impl TcpStream {
     /// take a look at `set_read_timeout` and `set_write_timeout`.
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_timeout(timeout_ms)
+        self.inner.set_timeout(timeout_ms)
     }
 
     /// Sets the timeout for read operations on this stream.
@@ -215,7 +199,7 @@ impl TcpStream {
     /// during the timeout period.
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_read_timeout(timeout_ms)
+        self.inner.set_read_timeout(timeout_ms)
     }
 
     /// Sets the timeout for write operations on this stream.
@@ -242,7 +226,7 @@ impl TcpStream {
     /// asynchronous fashion after the call to write returns.
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_write_timeout(timeout_ms)
+        self.inner.set_write_timeout(timeout_ms)
     }
 }
 
@@ -256,19 +240,19 @@ impl Clone for TcpStream {
     /// Instead, the first read will receive the first packet received, and the
     /// second read will receive the second packet.
     fn clone(&self) -> TcpStream {
-        TcpStream { obj: self.obj.clone() }
+        TcpStream { inner: self.inner.clone() }
     }
 }
 
 impl Reader for TcpStream {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.obj.read(buf).map_err(IoError::from_rtio_error)
+        self.inner.read(buf)
     }
 }
 
 impl Writer for TcpStream {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.obj.write(buf).map_err(IoError::from_rtio_error)
+        self.inner.write(buf)
     }
 }
 
@@ -309,7 +293,7 @@ impl Writer for TcpStream {
 /// # }
 /// ```
 pub struct TcpListener {
-    obj: Box<RtioTcpListener + Send>,
+    inner: TcpListenerImp,
 }
 
 impl TcpListener {
@@ -324,26 +308,20 @@ impl TcpListener {
     /// The address type can be any implementor of `ToSocketAddr` trait. See its
     /// documentation for concrete examples.
     pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<TcpListener> {
-        super::with_addresses_io(addr, |io, addr| io.tcp_bind(addr).map(|l| TcpListener { obj: l }))
+        super::with_addresses(addr, |addr| {
+            TcpListenerImp::bind(addr).map(|inner| TcpListener { inner: inner })
+        })
     }
 
     /// Returns the local socket address of this listener.
     pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
-        match self.obj.socket_name() {
-            Ok(rtio::SocketAddr { ip, port }) => {
-                Ok(SocketAddr { ip: super::from_rtio(ip), port: port })
-            }
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.socket_name()
     }
 }
 
 impl Listener<TcpStream, TcpAcceptor> for TcpListener {
     fn listen(self) -> IoResult<TcpAcceptor> {
-        match self.obj.listen() {
-            Ok(acceptor) => Ok(TcpAcceptor { obj: acceptor }),
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.listen(128).map(|a| TcpAcceptor { inner: a })
     }
 }
 
@@ -351,7 +329,7 @@ impl Listener<TcpStream, TcpAcceptor> for TcpListener {
 /// a `TcpListener`'s `listen` method, and this object can be used to accept new
 /// `TcpStream` instances.
 pub struct TcpAcceptor {
-    obj: Box<RtioTcpAcceptor + Send>,
+    inner: TcpAcceptorImp,
 }
 
 impl TcpAcceptor {
@@ -399,7 +377,7 @@ impl TcpAcceptor {
     /// ```
     #[experimental = "the type of the argument and name of this function are \
                       subject to change"]
-    pub fn set_timeout(&mut self, ms: Option<u64>) { self.obj.set_timeout(ms); }
+    pub fn set_timeout(&mut self, ms: Option<u64>) { self.inner.set_timeout(ms); }
 
     /// Closes the accepting capabilities of this acceptor.
     ///
@@ -445,16 +423,13 @@ impl TcpAcceptor {
     /// ```
     #[experimental]
     pub fn close_accept(&mut self) -> IoResult<()> {
-        self.obj.close_accept().map_err(IoError::from_rtio_error)
+        self.inner.close_accept()
     }
 }
 
 impl Acceptor<TcpStream> for TcpAcceptor {
     fn accept(&mut self) -> IoResult<TcpStream> {
-        match self.obj.accept(){
-            Ok(s) => Ok(TcpStream::new(s)),
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.accept().map(TcpStream::new)
     }
 }
 
@@ -473,7 +448,7 @@ impl Clone for TcpAcceptor {
     /// This function is useful for creating a handle to invoke `close_accept`
     /// on to wake up any other task blocked in `accept`.
     fn clone(&self) -> TcpAcceptor {
-        TcpAcceptor { obj: self.obj.clone() }
+        TcpAcceptor { inner: self.inner.clone() }
     }
 }
 
@@ -1112,8 +1087,6 @@ mod test {
 
     #[test]
     fn shutdown_smoke() {
-        use rt::rtio::RtioTcpStream;
-
         let addr = next_test_ip4();
         let a = TcpListener::bind(addr).unwrap().listen();
         spawn(proc() {
@@ -1124,7 +1097,7 @@ mod test {
         });
 
         let mut s = TcpStream::connect(addr).unwrap();
-        assert!(s.obj.close_write().is_ok());
+        assert!(s.inner.close_write().is_ok());
         assert!(s.write([1]).is_err());
         assert_eq!(s.read_to_end(), Ok(vec!(1)));
     }
diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs
index 4ae054beadb96..31b619896479b 100644
--- a/src/libstd/io/net/udp.rs
+++ b/src/libstd/io/net/udp.rs
@@ -17,13 +17,10 @@
 
 use clone::Clone;
 use io::net::ip::{SocketAddr, IpAddr, ToSocketAddr};
-use io::{Reader, Writer, IoResult, IoError};
-use kinds::Send;
-use boxed::Box;
+use io::{Reader, Writer, IoResult};
 use option::Option;
 use result::{Ok, Err};
-use rt::rtio::{RtioSocket, RtioUdpSocket, IoFactory};
-use rt::rtio;
+use sys::udp::UdpSocket as UdpSocketImp;
 
 /// A User Datagram Protocol socket.
 ///
@@ -60,7 +57,7 @@ use rt::rtio;
 /// }
 /// ```
 pub struct UdpSocket {
-    obj: Box<RtioUdpSocket + Send>,
+    inner: UdpSocketImp,
 }
 
 impl UdpSocket {
@@ -69,18 +66,15 @@ impl UdpSocket {
     /// Address type can be any implementor of `ToSocketAddr` trait. See its
     /// documentation for concrete examples.
     pub fn bind<A: ToSocketAddr>(addr: A) -> IoResult<UdpSocket> {
-        super::with_addresses_io(addr, |io, addr| io.udp_bind(addr).map(|s| UdpSocket { obj: s }))
+        super::with_addresses(addr, |addr| {
+            UdpSocketImp::bind(addr).map(|s| UdpSocket { inner: s })
+        })
     }
 
     /// Receives data from the socket. On success, returns the number of bytes
     /// read and the address from whence the data came.
     pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> {
-        match self.obj.recv_from(buf) {
-            Ok((amt, rtio::SocketAddr { ip, port })) => {
-                Ok((amt, SocketAddr { ip: super::from_rtio(ip), port: port }))
-            }
-            Err(e) => Err(IoError::from_rtio_error(e)),
-        }
+        self.inner.recv_from(buf)
     }
 
     /// Sends data on the socket to the given address. Returns nothing on
@@ -89,10 +83,7 @@ impl UdpSocket {
     /// Address type can be any implementor of `ToSocketAddr` trait. See its
     /// documentation for concrete examples.
     pub fn send_to<A: ToSocketAddr>(&mut self, buf: &[u8], addr: A) -> IoResult<()> {
-        super::with_addresses(addr, |addr| self.obj.send_to(buf, rtio::SocketAddr {
-            ip: super::to_rtio(addr.ip),
-            port: addr.port,
-        }).map_err(IoError::from_rtio_error))
+        super::with_addresses(addr, |addr| self.inner.send_to(buf, addr))
     }
 
     /// Creates a `UdpStream`, which allows use of the `Reader` and `Writer`
@@ -112,24 +103,19 @@ impl UdpSocket {
 
     /// Returns the socket address that this socket was created from.
     pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
-        match self.obj.socket_name() {
-            Ok(a) => Ok(SocketAddr { ip: super::from_rtio(a.ip), port: a.port }),
-            Err(e) => Err(IoError::from_rtio_error(e))
-        }
+        self.inner.socket_name()
     }
 
     /// Joins a multicast IP address (becomes a member of it)
     #[experimental]
     pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
-        let e = self.obj.join_multicast(super::to_rtio(multi));
-        e.map_err(IoError::from_rtio_error)
+        self.inner.join_multicast(multi)
     }
 
     /// Leaves a multicast IP address (drops membership from it)
     #[experimental]
     pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
-        let e = self.obj.leave_multicast(super::to_rtio(multi));
-        e.map_err(IoError::from_rtio_error)
+        self.inner.leave_multicast(multi)
     }
 
     /// Set the multicast loop flag to the specified value
@@ -137,33 +123,25 @@ impl UdpSocket {
     /// This lets multicast packets loop back to local sockets (if enabled)
     #[experimental]
     pub fn set_multicast_loop(&mut self, on: bool) -> IoResult<()> {
-        if on {
-            self.obj.loop_multicast_locally()
-        } else {
-            self.obj.dont_loop_multicast_locally()
-        }.map_err(IoError::from_rtio_error)
+        self.inner.set_multicast_loop(on)
     }
 
     /// Sets the multicast TTL
     #[experimental]
     pub fn set_multicast_ttl(&mut self, ttl: int) -> IoResult<()> {
-        self.obj.multicast_time_to_live(ttl).map_err(IoError::from_rtio_error)
+        self.inner.multicast_time_to_live(ttl)
     }
 
     /// Sets this socket's TTL
     #[experimental]
     pub fn set_ttl(&mut self, ttl: int) -> IoResult<()> {
-        self.obj.time_to_live(ttl).map_err(IoError::from_rtio_error)
+        self.inner.time_to_live(ttl)
     }
 
     /// Sets the broadcast flag on or off
     #[experimental]
     pub fn set_broadcast(&mut self, broadcast: bool) -> IoResult<()> {
-        if broadcast {
-            self.obj.hear_broadcasts()
-        } else {
-            self.obj.ignore_broadcasts()
-        }.map_err(IoError::from_rtio_error)
+        self.inner.set_broadcast(broadcast)
     }
 
     /// Sets the read/write timeout for this socket.
@@ -171,7 +149,7 @@ impl UdpSocket {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_timeout(timeout_ms)
+        self.inner.set_timeout(timeout_ms)
     }
 
     /// Sets the read timeout for this socket.
@@ -179,7 +157,7 @@ impl UdpSocket {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_read_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_read_timeout(timeout_ms)
+        self.inner.set_read_timeout(timeout_ms)
     }
 
     /// Sets the write timeout for this socket.
@@ -187,7 +165,7 @@ impl UdpSocket {
     /// For more information, see `TcpStream::set_timeout`
     #[experimental = "the timeout argument may change in type and value"]
     pub fn set_write_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.obj.set_write_timeout(timeout_ms)
+        self.inner.set_write_timeout(timeout_ms)
     }
 }
 
@@ -201,7 +179,7 @@ impl Clone for UdpSocket {
     /// received, and the second read will receive the second packet.
     fn clone(&self) -> UdpSocket {
         UdpSocket {
-            obj: self.obj.clone(),
+            inner: self.inner.clone(),
         }
     }
 }
diff --git a/src/libstd/io/pipe.rs b/src/libstd/io/pipe.rs
index c77cffd561e66..64b2518fab1c5 100644
--- a/src/libstd/io/pipe.rs
+++ b/src/libstd/io/pipe.rs
@@ -17,15 +17,17 @@
 
 use prelude::*;
 
-use io::{IoResult, IoError};
+use io::IoResult;
 use libc;
-use os;
-use rt::rtio::{RtioPipe, LocalIo};
+use sync::Arc;
+
+use sys_common;
+use sys;
+use sys::fs::FileDesc as FileDesc;
 
 /// A synchronous, in-memory pipe.
 pub struct PipeStream {
-    /// The internal, opaque runtime pipe object.
-    obj: Box<RtioPipe + Send>,
+    inner: Arc<FileDesc>
 }
 
 pub struct PipePair {
@@ -55,14 +57,14 @@ impl PipeStream {
     /// }
     /// ```
     pub fn open(fd: libc::c_int) -> IoResult<PipeStream> {
-        LocalIo::maybe_raise(|io| {
-            io.pipe_open(fd).map(|obj| PipeStream { obj: obj })
-        }).map_err(IoError::from_rtio_error)
+        Ok(PipeStream::from_filedesc(FileDesc::new(fd, true)))
     }
 
+    // FIXME: expose this some other way
+    /// Wrap a FileDesc directly, taking ownership.
     #[doc(hidden)]
-    pub fn new(inner: Box<RtioPipe + Send>) -> PipeStream {
-        PipeStream { obj: inner }
+    pub fn from_filedesc(fd: FileDesc) -> PipeStream {
+        PipeStream { inner: Arc::new(fd) }
     }
 
     /// Creates a pair of in-memory OS pipes for a unidirectional communication
@@ -76,43 +78,35 @@ impl PipeStream {
     /// This function can fail to succeed if the underlying OS has run out of
     /// available resources to allocate a new pipe.
     pub fn pair() -> IoResult<PipePair> {
-        struct Closer { fd: libc::c_int }
-
-        let os::Pipe { reader, writer } = try!(unsafe { os::pipe() });
-        let mut reader = Closer { fd: reader };
-        let mut writer = Closer { fd: writer };
-
-        let io_reader = try!(PipeStream::open(reader.fd));
-        reader.fd = -1;
-        let io_writer = try!(PipeStream::open(writer.fd));
-        writer.fd = -1;
-        return Ok(PipePair { reader: io_reader, writer: io_writer });
-
-        impl Drop for Closer {
-            fn drop(&mut self) {
-                if self.fd != -1 {
-                    let _ = unsafe { libc::close(self.fd) };
-                }
-            }
-        }
+        let (reader, writer) = try!(unsafe { sys::os::pipe() });
+        Ok(PipePair {
+            reader: PipeStream::from_filedesc(reader),
+            writer: PipeStream::from_filedesc(writer),
+        })
+    }
+}
+
+impl sys_common::AsFileDesc for PipeStream {
+    fn as_fd(&self) -> &sys::fs::FileDesc {
+        &*self.inner
     }
 }
 
 impl Clone for PipeStream {
     fn clone(&self) -> PipeStream {
-        PipeStream { obj: self.obj.clone() }
+        PipeStream { inner: self.inner.clone() }
     }
 }
 
 impl Reader for PipeStream {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.obj.read(buf).map_err(IoError::from_rtio_error)
+        self.inner.read(buf)
     }
 }
 
 impl Writer for PipeStream {
     fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.obj.write(buf).map_err(IoError::from_rtio_error)
+        self.inner.write(buf)
     }
 }
 
diff --git a/src/libstd/os.rs b/src/libstd/os.rs
index 175e23bf8192a..ea42117bab601 100644
--- a/src/libstd/os.rs
+++ b/src/libstd/os.rs
@@ -43,6 +43,7 @@ use ops::Drop;
 use option::{Some, None, Option};
 use os;
 use path::{Path, GenericPath, BytesContainer};
+use sys;
 use sys::os as os_imp;
 use ptr::RawPtr;
 use ptr;
@@ -603,35 +604,11 @@ pub struct Pipe {
 /// descriptors to be closed, the file descriptors will leak. For safe handling
 /// of this scenario, use `std::io::PipeStream` instead.
 pub unsafe fn pipe() -> IoResult<Pipe> {
-    return _pipe();
-
-    #[cfg(unix)]
-    unsafe fn _pipe() -> IoResult<Pipe> {
-        let mut fds = [0, ..2];
-        match libc::pipe(fds.as_mut_ptr()) {
-            0 => Ok(Pipe { reader: fds[0], writer: fds[1] }),
-            _ => Err(IoError::last_error()),
-        }
-    }
-
-    #[cfg(windows)]
-    unsafe fn _pipe() -> IoResult<Pipe> {
-        // Windows pipes work subtly differently than unix pipes, and their
-        // inheritance has to be handled in a different way that I do not
-        // fully understand. Here we explicitly make the pipe non-inheritable,
-        // which means to pass it to a subprocess they need to be duplicated
-        // first, as in std::run.
-        let mut fds = [0, ..2];
-        match libc::pipe(fds.as_mut_ptr(), 1024 as ::libc::c_uint,
-                         (libc::O_BINARY | libc::O_NOINHERIT) as c_int) {
-            0 => {
-                assert!(fds[0] != -1 && fds[0] != 0);
-                assert!(fds[1] != -1 && fds[1] != 0);
-                Ok(Pipe { reader: fds[0], writer: fds[1] })
-            }
-            _ => Err(IoError::last_error()),
-        }
-    }
+    let (reader, writer) = try!(sys::os::pipe());
+    Ok(Pipe {
+        reader: reader.unwrap(),
+        writer: writer.unwrap(),
+    })
 }
 
 /// Returns the proper dll filename for the given basename of a file
diff --git a/src/libnative/io/net.rs b/src/libstd/sys/common/net.rs
similarity index 53%
rename from src/libnative/io/net.rs
rename to src/libstd/sys/common/net.rs
index a4b97a3eb84ef..0559005100f90 100644
--- a/src/libnative/io/net.rs
+++ b/src/libstd/sys/common/net.rs
@@ -9,21 +9,26 @@
 // except according to those terms.
 
 use alloc::arc::Arc;
-use libc;
-use std::mem;
-use std::ptr;
-use std::rt::mutex;
-use std::rt::rtio::{mod, IoResult, IoError};
-use std::sync::atomic;
-
-use super::{retry, keep_going};
-use super::c;
-use super::util;
-
-#[cfg(unix)] use super::process;
-#[cfg(unix)] use super::file::FileDesc;
-
-pub use self::os::{init, sock_t, last_error};
+use libc::{mod, c_char, c_int};
+use mem;
+use ptr::{mod, null, null_mut};
+use rt::mutex;
+use io::net::ip::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
+use io::net::addrinfo;
+use io::{IoResult, IoError};
+use sys::{mod, retry, c, sock_t, last_error, last_net_error, last_gai_error, close_sock,
+          wrlen, msglen_t, os, wouldblock, set_nonblocking, timer, ms_to_timeval,
+          decode_error_detailed};
+use sys_common::{mod, keep_going, short_write, timeout};
+use prelude::*;
+use cmp;
+use io;
+
+#[deriving(Show)]
+pub enum SocketStatus {
+    Readable,
+    Writable,
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 // sockaddr and misc bindings
@@ -36,14 +41,14 @@ pub fn ntohs(u: u16) -> u16 {
     Int::from_be(u)
 }
 
-enum InAddr {
+pub enum InAddr {
     In4Addr(libc::in_addr),
     In6Addr(libc::in6_addr),
 }
 
-fn ip_to_inaddr(ip: rtio::IpAddr) -> InAddr {
+pub fn ip_to_inaddr(ip: IpAddr) -> InAddr {
     match ip {
-        rtio::Ipv4Addr(a, b, c, d) => {
+        Ipv4Addr(a, b, c, d) => {
             let ip = (a as u32 << 24) |
                      (b as u32 << 16) |
                      (c as u32 <<  8) |
@@ -52,7 +57,7 @@ fn ip_to_inaddr(ip: rtio::IpAddr) -> InAddr {
                 s_addr: Int::from_be(ip)
             })
         }
-        rtio::Ipv6Addr(a, b, c, d, e, f, g, h) => {
+        Ipv6Addr(a, b, c, d, e, f, g, h) => {
             In6Addr(libc::in6_addr {
                 s6_addr: [
                     htons(a),
@@ -69,7 +74,7 @@ fn ip_to_inaddr(ip: rtio::IpAddr) -> InAddr {
     }
 }
 
-fn addr_to_sockaddr(addr: rtio::SocketAddr,
+pub fn addr_to_sockaddr(addr: SocketAddr,
                     storage: &mut libc::sockaddr_storage)
                     -> libc::socklen_t {
     unsafe {
@@ -93,20 +98,20 @@ fn addr_to_sockaddr(addr: rtio::SocketAddr,
     }
 }
 
-fn socket(addr: rtio::SocketAddr, ty: libc::c_int) -> IoResult<sock_t> {
+pub fn socket(addr: SocketAddr, ty: libc::c_int) -> IoResult<sock_t> {
     unsafe {
         let fam = match addr.ip {
-            rtio::Ipv4Addr(..) => libc::AF_INET,
-            rtio::Ipv6Addr(..) => libc::AF_INET6,
+            Ipv4Addr(..) => libc::AF_INET,
+            Ipv6Addr(..) => libc::AF_INET6,
         };
         match libc::socket(fam, ty, 0) {
-            -1 => Err(os::last_error()),
+            -1 => Err(last_net_error()),
             fd => Ok(fd),
         }
     }
 }
 
-fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int,
+pub fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int,
                  payload: T) -> IoResult<()> {
     unsafe {
         let payload = &payload as *const T as *const libc::c_void;
@@ -114,7 +119,7 @@ fn setsockopt<T>(fd: sock_t, opt: libc::c_int, val: libc::c_int,
                                    payload,
                                    mem::size_of::<T>() as libc::socklen_t);
         if ret != 0 {
-            Err(os::last_error())
+            Err(last_net_error())
         } else {
             Ok(())
         }
@@ -130,7 +135,7 @@ pub fn getsockopt<T: Copy>(fd: sock_t, opt: libc::c_int,
                                 &mut slot as *mut _ as *mut _,
                                 &mut len);
         if ret != 0 {
-            Err(os::last_error())
+            Err(last_net_error())
         } else {
             assert!(len as uint == mem::size_of::<T>());
             Ok(slot)
@@ -138,10 +143,10 @@ pub fn getsockopt<T: Copy>(fd: sock_t, opt: libc::c_int,
     }
 }
 
-fn sockname(fd: sock_t,
+pub fn sockname(fd: sock_t,
             f: unsafe extern "system" fn(sock_t, *mut libc::sockaddr,
                                          *mut libc::socklen_t) -> libc::c_int)
-    -> IoResult<rtio::SocketAddr>
+    -> IoResult<SocketAddr>
 {
     let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
     let mut len = mem::size_of::<libc::sockaddr_storage>() as libc::socklen_t;
@@ -151,14 +156,14 @@ fn sockname(fd: sock_t,
                     storage as *mut libc::sockaddr,
                     &mut len as *mut libc::socklen_t);
         if ret != 0 {
-            return Err(os::last_error())
+            return Err(last_net_error())
         }
     }
     return sockaddr_to_addr(&storage, len as uint);
 }
 
 pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
-                        len: uint) -> IoResult<rtio::SocketAddr> {
+                        len: uint) -> IoResult<SocketAddr> {
     match storage.ss_family as libc::c_int {
         libc::AF_INET => {
             assert!(len as uint >= mem::size_of::<libc::sockaddr_in>());
@@ -170,8 +175,8 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
             let b = (ip >> 16) as u8;
             let c = (ip >>  8) as u8;
             let d = (ip >>  0) as u8;
-            Ok(rtio::SocketAddr {
-                ip: rtio::Ipv4Addr(a, b, c, d),
+            Ok(SocketAddr {
+                ip: Ipv4Addr(a, b, c, d),
                 port: ntohs(storage.sin_port),
             })
         }
@@ -188,17 +193,15 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
             let f = ntohs(storage.sin6_addr.s6_addr[5]);
             let g = ntohs(storage.sin6_addr.s6_addr[6]);
             let h = ntohs(storage.sin6_addr.s6_addr[7]);
-            Ok(rtio::SocketAddr {
-                ip: rtio::Ipv6Addr(a, b, c, d, e, f, g, h),
+            Ok(SocketAddr {
+                ip: Ipv6Addr(a, b, c, d, e, f, g, h),
                 port: ntohs(storage.sin6_port),
             })
         }
         _ => {
-            #[cfg(unix)] use libc::EINVAL as ERROR;
-            #[cfg(windows)] use libc::WSAEINVAL as ERROR;
             Err(IoError {
-                code: ERROR as uint,
-                extra: 0,
+                kind: io::InvalidInput,
+                desc: "invalid argument",
                 detail: None,
             })
         }
@@ -206,15 +209,343 @@ pub fn sockaddr_to_addr(storage: &libc::sockaddr_storage,
 }
 
 ////////////////////////////////////////////////////////////////////////////////
-// TCP streams
+// get_host_addresses
 ////////////////////////////////////////////////////////////////////////////////
 
-pub struct TcpStream {
-    inner: Arc<Inner>,
-    read_deadline: u64,
-    write_deadline: u64,
+extern "system" {
+    fn getaddrinfo(node: *const c_char, service: *const c_char,
+                   hints: *const libc::addrinfo,
+                   res: *mut *mut libc::addrinfo) -> c_int;
+    fn freeaddrinfo(res: *mut libc::addrinfo);
 }
 
+pub fn get_host_addresses(host: Option<&str>, servname: Option<&str>,
+                          hint: Option<addrinfo::Hint>)
+                          -> Result<Vec<addrinfo::Info>, IoError>
+{
+    sys::init_net();
+
+    assert!(host.is_some() || servname.is_some());
+
+    let c_host = host.map(|x| x.to_c_str());
+    let c_host = c_host.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
+    let c_serv = servname.map(|x| x.to_c_str());
+    let c_serv = c_serv.as_ref().map(|x| x.as_ptr()).unwrap_or(null());
+
+    let hint = hint.map(|hint| {
+        libc::addrinfo {
+            ai_flags: hint.flags as c_int,
+            ai_family: hint.family as c_int,
+            ai_socktype: 0,
+            ai_protocol: 0,
+            ai_addrlen: 0,
+            ai_canonname: null_mut(),
+            ai_addr: null_mut(),
+            ai_next: null_mut()
+        }
+    });
+
+    let hint_ptr = hint.as_ref().map_or(null(), |x| {
+        x as *const libc::addrinfo
+    });
+    let mut res = null_mut();
+
+    // Make the call
+    let s = unsafe {
+        getaddrinfo(c_host, c_serv, hint_ptr, &mut res)
+    };
+
+    // Error?
+    if s != 0 {
+        return Err(last_gai_error(s));
+    }
+
+    // Collect all the results we found
+    let mut addrs = Vec::new();
+    let mut rp = res;
+    while rp.is_not_null() {
+        unsafe {
+            let addr = try!(sockaddr_to_addr(mem::transmute((*rp).ai_addr),
+                                             (*rp).ai_addrlen as uint));
+            addrs.push(addrinfo::Info {
+                address: addr,
+                family: (*rp).ai_family as uint,
+                socktype: None,
+                protocol: None,
+                flags: (*rp).ai_flags as uint
+            });
+
+            rp = (*rp).ai_next as *mut libc::addrinfo;
+        }
+    }
+
+    unsafe { freeaddrinfo(res); }
+
+    Ok(addrs)
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Timeout helpers
+//
+// The read/write functions below are the helpers for reading/writing a socket
+// with a possible deadline specified. This is generally viewed as a timed out
+// I/O operation.
+//
+// From the application's perspective, timeouts apply to the I/O object, not to
+// the underlying file descriptor (it's one timeout per object). This means that
+// we can't use the SO_RCVTIMEO and corresponding send timeout option.
+//
+// The next idea to implement timeouts would be to use nonblocking I/O. An
+// invocation of select() would wait (with a timeout) for a socket to be ready.
+// Once its ready, we can perform the operation. Note that the operation *must*
+// be nonblocking, even though select() says the socket is ready. This is
+// because some other thread could have come and stolen our data (handles can be
+// cloned).
+//
+// To implement nonblocking I/O, the first option we have is to use the
+// O_NONBLOCK flag. Remember though that this is a global setting, affecting all
+// I/O objects, so this was initially viewed as unwise.
+//
+// It turns out that there's this nifty MSG_DONTWAIT flag which can be passed to
+// send/recv, but the niftiness wears off once you realize it only works well on
+// Linux [1] [2]. This means that it's pretty easy to get a nonblocking
+// operation on Linux (no flag fiddling, no affecting other objects), but not on
+// other platforms.
+//
+// To work around this constraint on other platforms, we end up using the
+// original strategy of flipping the O_NONBLOCK flag. As mentioned before, this
+// could cause other objects' blocking operations to suddenly become
+// nonblocking. To get around this, a "blocking operation" which returns EAGAIN
+// falls back to using the same code path as nonblocking operations, but with an
+// infinite timeout (select + send/recv). This helps emulate blocking
+// reads/writes despite the underlying descriptor being nonblocking, as well as
+// optimizing the fast path of just hitting one syscall in the good case.
+//
+// As a final caveat, this implementation uses a mutex so only one thread is
+// doing a nonblocking operation at at time. This is the operation that comes
+// after the select() (at which point we think the socket is ready). This is
+// done for sanity to ensure that the state of the O_NONBLOCK flag is what we
+// expect (wouldn't want someone turning it on when it should be off!). All
+// operations performed in the lock are *nonblocking* to avoid holding the mutex
+// forever.
+//
+// So, in summary, Linux uses MSG_DONTWAIT and doesn't need mutexes, everyone
+// else uses O_NONBLOCK and mutexes with some trickery to make sure blocking
+// reads/writes are still blocking.
+//
+// Fun, fun!
+//
+// [1] http://twistedmatrix.com/pipermail/twisted-commits/2012-April/034692.html
+// [2] http://stackoverflow.com/questions/19819198/does-send-msg-dontwait
+
+pub fn read<T>(fd: sock_t,
+               deadline: u64,
+               lock: || -> T,
+               read: |bool| -> libc::c_int) -> IoResult<uint> {
+    let mut ret = -1;
+    if deadline == 0 {
+        ret = retry(|| read(false));
+    }
+
+    if deadline != 0 || (ret == -1 && wouldblock()) {
+        let deadline = match deadline {
+            0 => None,
+            n => Some(n),
+        };
+        loop {
+            // With a timeout, first we wait for the socket to become
+            // readable using select(), specifying the relevant timeout for
+            // our previously set deadline.
+            try!(await([fd], deadline, Readable));
+
+            // At this point, we're still within the timeout, and we've
+            // determined that the socket is readable (as returned by
+            // select). We must still read the socket in *nonblocking* mode
+            // because some other thread could come steal our data. If we
+            // fail to read some data, we retry (hence the outer loop) and
+            // wait for the socket to become readable again.
+            let _guard = lock();
+            match retry(|| read(deadline.is_some())) {
+                -1 if wouldblock() => {}
+                -1 => return Err(last_net_error()),
+               n => { ret = n; break }
+            }
+        }
+    }
+
+    match ret {
+        0 => Err(sys_common::eof()),
+        n if n < 0 => Err(last_net_error()),
+        n => Ok(n as uint)
+    }
+}
+
+pub fn write<T>(fd: sock_t,
+                deadline: u64,
+                buf: &[u8],
+                write_everything: bool,
+                lock: || -> T,
+                write: |bool, *const u8, uint| -> i64) -> IoResult<uint> {
+    let mut ret = -1;
+    let mut written = 0;
+    if deadline == 0 {
+        if write_everything {
+            ret = keep_going(buf, |inner, len| {
+                written = buf.len() - len;
+                write(false, inner, len)
+            });
+        } else {
+            ret = retry(|| { write(false, buf.as_ptr(), buf.len()) });
+            if ret > 0 { written = ret as uint; }
+        }
+    }
+
+    if deadline != 0 || (ret == -1 && wouldblock()) {
+        let deadline = match deadline {
+            0 => None,
+            n => Some(n),
+        };
+        while written < buf.len() && (write_everything || written == 0) {
+            // As with read(), first wait for the socket to be ready for
+            // the I/O operation.
+            match await([fd], deadline, Writable) {
+                Err(ref e) if e.kind == io::EndOfFile && written > 0 => {
+                    assert!(deadline.is_some());
+                    return Err(short_write(written, "short write"))
+                }
+                Err(e) => return Err(e),
+                Ok(()) => {}
+            }
+
+            // Also as with read(), we use MSG_DONTWAIT to guard ourselves
+            // against unforeseen circumstances.
+            let _guard = lock();
+            let ptr = buf[written..].as_ptr();
+            let len = buf.len() - written;
+            match retry(|| write(deadline.is_some(), ptr, len)) {
+                -1 if wouldblock() => {}
+                -1 => return Err(last_net_error()),
+                n => { written += n as uint; }
+            }
+        }
+        ret = 0;
+    }
+    if ret < 0 {
+        Err(last_net_error())
+    } else {
+        Ok(written)
+    }
+}
+
+// See http://developerweb.net/viewtopic.php?id=3196 for where this is
+// derived from.
+pub fn connect_timeout(fd: sock_t,
+                       addrp: *const libc::sockaddr,
+                       len: libc::socklen_t,
+                       timeout_ms: u64) -> IoResult<()> {
+    #[cfg(unix)]    use libc::EINPROGRESS as INPROGRESS;
+    #[cfg(windows)] use libc::WSAEINPROGRESS as INPROGRESS;
+    #[cfg(unix)]    use libc::EWOULDBLOCK as WOULDBLOCK;
+    #[cfg(windows)] use libc::WSAEWOULDBLOCK as WOULDBLOCK;
+
+    // Make sure the call to connect() doesn't block
+    try!(set_nonblocking(fd, true));
+
+    let ret = match unsafe { libc::connect(fd, addrp, len) } {
+        // If the connection is in progress, then we need to wait for it to
+        // finish (with a timeout). The current strategy for doing this is
+        // to use select() with a timeout.
+        -1 if os::errno() as int == INPROGRESS as int ||
+              os::errno() as int == WOULDBLOCK as int => {
+            let mut set: c::fd_set = unsafe { mem::zeroed() };
+            c::fd_set(&mut set, fd);
+            match await(fd, &mut set, timeout_ms) {
+                0 => Err(timeout("connection timed out")),
+                -1 => Err(last_net_error()),
+                _ => {
+                    let err: libc::c_int = try!(
+                        getsockopt(fd, libc::SOL_SOCKET, libc::SO_ERROR));
+                    if err == 0 {
+                        Ok(())
+                    } else {
+                        Err(decode_error_detailed(err))
+                    }
+                }
+            }
+        }
+
+        -1 => Err(last_net_error()),
+        _ => Ok(()),
+    };
+
+    // be sure to turn blocking I/O back on
+    try!(set_nonblocking(fd, false));
+    return ret;
+
+    #[cfg(unix)]
+    fn await(fd: sock_t, set: &mut c::fd_set, timeout: u64) -> libc::c_int {
+        let start = timer::now();
+        retry(|| unsafe {
+            // Recalculate the timeout each iteration (it is generally
+            // undefined what the value of the 'tv' is after select
+            // returns EINTR).
+            let mut tv = ms_to_timeval(timeout - (timer::now() - start));
+            c::select(fd + 1, ptr::null_mut(), set as *mut _,
+                      ptr::null_mut(), &mut tv)
+        })
+    }
+    #[cfg(windows)]
+    fn await(_fd: sock_t, set: &mut c::fd_set, timeout: u64) -> libc::c_int {
+        let mut tv = ms_to_timeval(timeout);
+        unsafe { c::select(1, ptr::null_mut(), set, ptr::null_mut(), &mut tv) }
+    }
+}
+
+pub fn await(fds: &[sock_t], deadline: Option<u64>,
+             status: SocketStatus) -> IoResult<()> {
+    let mut set: c::fd_set = unsafe { mem::zeroed() };
+    let mut max = 0;
+    for &fd in fds.iter() {
+        c::fd_set(&mut set, fd);
+        max = cmp::max(max, fd + 1);
+    }
+    if cfg!(windows) {
+        max = fds.len() as sock_t;
+    }
+
+    let (read, write) = match status {
+        Readable => (&mut set as *mut _, ptr::null_mut()),
+        Writable => (ptr::null_mut(), &mut set as *mut _),
+    };
+    let mut tv: libc::timeval = unsafe { mem::zeroed() };
+
+    match retry(|| {
+        let now = timer::now();
+        let tvp = match deadline {
+            None => ptr::null_mut(),
+            Some(deadline) => {
+                // If we're past the deadline, then pass a 0 timeout to
+                // select() so we can poll the status
+                let ms = if deadline < now {0} else {deadline - now};
+                tv = ms_to_timeval(ms);
+                &mut tv as *mut _
+            }
+        };
+        let r = unsafe {
+            c::select(max as libc::c_int, read, write, ptr::null_mut(), tvp)
+        };
+        r
+    }) {
+        -1 => Err(last_net_error()),
+        0 => Err(timeout("timed out")),
+        _ => Ok(()),
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Basic socket representation
+////////////////////////////////////////////////////////////////////////////////
+
 struct Inner {
     fd: sock_t,
 
@@ -223,22 +554,44 @@ struct Inner {
     lock: mutex::NativeMutex
 }
 
+impl Inner {
+    fn new(fd: sock_t) -> Inner {
+        Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } }
+    }
+}
+
+impl Drop for Inner {
+    fn drop(&mut self) { unsafe { close_sock(self.fd); } }
+}
+
 pub struct Guard<'a> {
     pub fd: sock_t,
     pub guard: mutex::LockGuard<'a>,
 }
 
-impl Inner {
-    fn new(fd: sock_t) -> Inner {
-        Inner { fd: fd, lock: unsafe { mutex::NativeMutex::new() } }
+#[unsafe_destructor]
+impl<'a> Drop for Guard<'a> {
+    fn drop(&mut self) {
+        assert!(set_nonblocking(self.fd, false).is_ok());
     }
 }
 
+////////////////////////////////////////////////////////////////////////////////
+// TCP streams
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct TcpStream {
+    inner: Arc<Inner>,
+    read_deadline: u64,
+    write_deadline: u64,
+}
+
 impl TcpStream {
-    pub fn connect(addr: rtio::SocketAddr,
-                   timeout: Option<u64>) -> IoResult<TcpStream> {
+    pub fn connect(addr: SocketAddr, timeout: Option<u64>) -> IoResult<TcpStream> {
+        sys::init_net();
+
         let fd = try!(socket(addr, libc::SOCK_STREAM));
-        let ret = TcpStream::new(Inner::new(fd));
+        let ret = TcpStream::new(fd);
 
         let mut storage = unsafe { mem::zeroed() };
         let len = addr_to_sockaddr(addr, &mut storage);
@@ -246,21 +599,21 @@ impl TcpStream {
 
         match timeout {
             Some(timeout) => {
-                try!(util::connect_timeout(fd, addrp, len, timeout));
+                try!(connect_timeout(fd, addrp, len, timeout));
                 Ok(ret)
             },
             None => {
                 match retry(|| unsafe { libc::connect(fd, addrp, len) }) {
-                    -1 => Err(os::last_error()),
+                    -1 => Err(last_error()),
                     _ => Ok(ret),
                 }
             }
         }
     }
 
-    fn new(inner: Inner) -> TcpStream {
+    pub fn new(fd: sock_t) -> TcpStream {
         TcpStream {
-            inner: Arc::new(inner),
+            inner: Arc::new(Inner::new(fd)),
             read_deadline: 0,
             write_deadline: 0,
         }
@@ -268,12 +621,12 @@ impl TcpStream {
 
     pub fn fd(&self) -> sock_t { self.inner.fd }
 
-    fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
+    pub fn set_nodelay(&mut self, nodelay: bool) -> IoResult<()> {
         setsockopt(self.fd(), libc::IPPROTO_TCP, libc::TCP_NODELAY,
                    nodelay as libc::c_int)
     }
 
-    fn set_keepalive(&mut self, seconds: Option<uint>) -> IoResult<()> {
+    pub fn set_keepalive(&mut self, seconds: Option<uint>) -> IoResult<()> {
         let ret = setsockopt(self.fd(), libc::SOL_SOCKET, libc::SO_KEEPALIVE,
                              seconds.is_some() as libc::c_int);
         match seconds {
@@ -309,16 +662,11 @@ impl TcpStream {
             fd: self.fd(),
             guard: unsafe { self.inner.lock.lock() },
         };
-        assert!(util::set_nonblocking(self.fd(), true).is_ok());
+        assert!(set_nonblocking(self.fd(), true).is_ok());
         ret
     }
-}
 
-#[cfg(windows)] type wrlen = libc::c_int;
-#[cfg(not(windows))] type wrlen = libc::size_t;
-
-impl rtio::RtioTcpStream for TcpStream {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
         let fd = self.fd();
         let dolock = || self.lock_nonblocking();
         let doread = |nb| unsafe {
@@ -331,7 +679,7 @@ impl rtio::RtioTcpStream for TcpStream {
         read(fd, self.read_deadline, dolock, doread)
     }
 
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         let fd = self.fd();
         let dolock = || self.lock_nonblocking();
         let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe {
@@ -341,340 +689,42 @@ impl rtio::RtioTcpStream for TcpStream {
                        len as wrlen,
                        flags) as i64
         };
-        match write(fd, self.write_deadline, buf, true, dolock, dowrite) {
-            Ok(_) => Ok(()),
-            Err(e) => Err(e)
-        }
+        write(fd, self.write_deadline, buf, true, dolock, dowrite).map(|_| ())
     }
-    fn peer_name(&mut self) -> IoResult<rtio::SocketAddr> {
+    pub fn peer_name(&mut self) -> IoResult<SocketAddr> {
         sockname(self.fd(), libc::getpeername)
     }
-    fn control_congestion(&mut self) -> IoResult<()> {
-        self.set_nodelay(false)
-    }
-    fn nodelay(&mut self) -> IoResult<()> {
-        self.set_nodelay(true)
-    }
-    fn keepalive(&mut self, delay_in_seconds: uint) -> IoResult<()> {
-        self.set_keepalive(Some(delay_in_seconds))
-    }
-    fn letdie(&mut self) -> IoResult<()> {
-        self.set_keepalive(None)
-    }
 
-    fn clone(&self) -> Box<rtio::RtioTcpStream + Send> {
-        box TcpStream {
-            inner: self.inner.clone(),
-            read_deadline: 0,
-            write_deadline: 0,
-        } as Box<rtio::RtioTcpStream + Send>
-    }
-
-    fn close_write(&mut self) -> IoResult<()> {
+    pub fn close_write(&mut self) -> IoResult<()> {
         super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
     }
-    fn close_read(&mut self) -> IoResult<()> {
+    pub fn close_read(&mut self) -> IoResult<()> {
         super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
     }
 
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
         self.read_deadline = deadline;
         self.write_deadline = deadline;
     }
-    fn set_read_timeout(&mut self, timeout: Option<u64>) {
-        self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
-    }
-    fn set_write_timeout(&mut self, timeout: Option<u64>) {
-        self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
-    }
-}
-
-impl rtio::RtioSocket for TcpStream {
-    fn socket_name(&mut self) -> IoResult<rtio::SocketAddr> {
-        sockname(self.fd(), libc::getsockname)
-    }
-}
-
-impl Drop for Inner {
-    fn drop(&mut self) { unsafe { os::close(self.fd); } }
-}
-
-#[unsafe_destructor]
-impl<'a> Drop for Guard<'a> {
-    fn drop(&mut self) {
-        assert!(util::set_nonblocking(self.fd, false).is_ok());
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// TCP listeners
-////////////////////////////////////////////////////////////////////////////////
-
-pub struct TcpListener {
-    inner: Inner,
-}
-
-impl TcpListener {
-    pub fn bind(addr: rtio::SocketAddr) -> IoResult<TcpListener> {
-        let fd = try!(socket(addr, libc::SOCK_STREAM));
-        let ret = TcpListener { inner: Inner::new(fd) };
-
-        let mut storage = unsafe { mem::zeroed() };
-        let len = addr_to_sockaddr(addr, &mut storage);
-        let addrp = &storage as *const _ as *const libc::sockaddr;
-
-        // On platforms with Berkeley-derived sockets, this allows
-        // to quickly rebind a socket, without needing to wait for
-        // the OS to clean up the previous one.
-        if cfg!(unix) {
-            try!(setsockopt(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR,
-                            1 as libc::c_int));
-        }
-
-        match unsafe { libc::bind(fd, addrp, len) } {
-            -1 => Err(os::last_error()),
-            _ => Ok(ret),
-        }
-    }
-
-    pub fn fd(&self) -> sock_t { self.inner.fd }
-
-    pub fn native_listen(self, backlog: int) -> IoResult<TcpAcceptor> {
-        match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
-            -1 => Err(os::last_error()),
-
-            #[cfg(unix)]
-            _ => {
-                let (reader, writer) = try!(process::pipe());
-                try!(util::set_nonblocking(reader.fd(), true));
-                try!(util::set_nonblocking(writer.fd(), true));
-                try!(util::set_nonblocking(self.fd(), true));
-                Ok(TcpAcceptor {
-                    inner: Arc::new(AcceptorInner {
-                        listener: self,
-                        reader: reader,
-                        writer: writer,
-                        closed: atomic::AtomicBool::new(false),
-                    }),
-                    deadline: 0,
-                })
-            }
-
-            #[cfg(windows)]
-            _ => {
-                let accept = try!(os::Event::new());
-                let ret = unsafe {
-                    c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT)
-                };
-                if ret != 0 {
-                    return Err(os::last_error())
-                }
-                Ok(TcpAcceptor {
-                    inner: Arc::new(AcceptorInner {
-                        listener: self,
-                        abort: try!(os::Event::new()),
-                        accept: accept,
-                        closed: atomic::AtomicBool::new(false),
-                    }),
-                    deadline: 0,
-                })
-            }
-        }
-    }
-}
-
-impl rtio::RtioTcpListener for TcpListener {
-    fn listen(self: Box<TcpListener>)
-              -> IoResult<Box<rtio::RtioTcpAcceptor + Send>> {
-        self.native_listen(128).map(|a| {
-            box a as Box<rtio::RtioTcpAcceptor + Send>
-        })
-    }
-}
-
-impl rtio::RtioSocket for TcpListener {
-    fn socket_name(&mut self) -> IoResult<rtio::SocketAddr> {
-        sockname(self.fd(), libc::getsockname)
-    }
-}
-
-pub struct TcpAcceptor {
-    inner: Arc<AcceptorInner>,
-    deadline: u64,
-}
-
-#[cfg(unix)]
-struct AcceptorInner {
-    listener: TcpListener,
-    reader: FileDesc,
-    writer: FileDesc,
-    closed: atomic::AtomicBool,
-}
-
-#[cfg(windows)]
-struct AcceptorInner {
-    listener: TcpListener,
-    abort: os::Event,
-    accept: os::Event,
-    closed: atomic::AtomicBool,
-}
-
-impl TcpAcceptor {
-    pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
-
-    #[cfg(unix)]
-    pub fn native_accept(&mut self) -> IoResult<TcpStream> {
-        // In implementing accept, the two main concerns are dealing with
-        // close_accept() and timeouts. The unix implementation is based on a
-        // nonblocking accept plus a call to select(). Windows ends up having
-        // an entirely separate implementation than unix, which is explained
-        // below.
-        //
-        // To implement timeouts, all blocking is done via select() instead of
-        // accept() by putting the socket in non-blocking mode. Because
-        // select() takes a timeout argument, we just pass through the timeout
-        // to select().
-        //
-        // To implement close_accept(), we have a self-pipe to ourselves which
-        // is passed to select() along with the socket being accepted on. The
-        // self-pipe is never written to unless close_accept() is called.
-        let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
-
-        while !self.inner.closed.load(atomic::SeqCst) {
-            match retry(|| unsafe {
-                libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
-            }) {
-                -1 if util::wouldblock() => {}
-                -1 => return Err(os::last_error()),
-                fd => return Ok(TcpStream::new(Inner::new(fd as sock_t))),
-            }
-            try!(util::await([self.fd(), self.inner.reader.fd()],
-                             deadline, util::Readable));
-        }
-
-        Err(util::eof())
+    pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
+        self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
-
-    #[cfg(windows)]
-    pub fn native_accept(&mut self) -> IoResult<TcpStream> {
-        // Unlink unix, windows cannot invoke `select` on arbitrary file
-        // descriptors like pipes, only sockets. Consequently, windows cannot
-        // use the same implementation as unix for accept() when close_accept()
-        // is considered.
-        //
-        // In order to implement close_accept() and timeouts, windows uses
-        // event handles. An acceptor-specific abort event is created which
-        // will only get set in close_accept(), and it will never be un-set.
-        // Additionally, another acceptor-specific event is associated with the
-        // FD_ACCEPT network event.
-        //
-        // These two events are then passed to WaitForMultipleEvents to see
-        // which one triggers first, and the timeout passed to this function is
-        // the local timeout for the acceptor.
-        //
-        // If the wait times out, then the accept timed out. If the wait
-        // succeeds with the abort event, then we were closed, and if the wait
-        // succeeds otherwise, then we do a nonblocking poll via `accept` to
-        // see if we can accept a connection. The connection is candidate to be
-        // stolen, so we do all of this in a loop as well.
-        let events = [self.inner.abort.handle(), self.inner.accept.handle()];
-
-        while !self.inner.closed.load(atomic::SeqCst) {
-            let ms = if self.deadline == 0 {
-                c::WSA_INFINITE as u64
-            } else {
-                let now = ::io::timer::now();
-                if self.deadline < now {0} else {self.deadline - now}
-            };
-            let ret = unsafe {
-                c::WSAWaitForMultipleEvents(2, events.as_ptr(), libc::FALSE,
-                                            ms as libc::DWORD, libc::FALSE)
-            };
-            match ret {
-                c::WSA_WAIT_TIMEOUT => {
-                    return Err(util::timeout("accept timed out"))
-                }
-                c::WSA_WAIT_FAILED => return Err(os::last_error()),
-                c::WSA_WAIT_EVENT_0 => break,
-                n => assert_eq!(n, c::WSA_WAIT_EVENT_0 + 1),
-            }
-
-            let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() };
-            let ret = unsafe {
-                c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents)
-            };
-            if ret != 0 { return Err(os::last_error()) }
-
-            if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue }
-            match unsafe {
-                libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
-            } {
-                -1 if util::wouldblock() => {}
-                -1 => return Err(os::last_error()),
-
-                // Accepted sockets inherit the same properties as the caller,
-                // so we need to deregister our event and switch the socket back
-                // to blocking mode
-                fd => {
-                    let stream = TcpStream::new(Inner::new(fd));
-                    let ret = unsafe {
-                        c::WSAEventSelect(fd, events[1], 0)
-                    };
-                    if ret != 0 { return Err(os::last_error()) }
-                    try!(util::set_nonblocking(fd, false));
-                    return Ok(stream)
-                }
-            }
-        }
-
-        Err(util::eof())
+    pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
+        self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
-}
 
-impl rtio::RtioSocket for TcpAcceptor {
-    fn socket_name(&mut self) -> IoResult<rtio::SocketAddr> {
+    pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
         sockname(self.fd(), libc::getsockname)
     }
 }
 
-impl rtio::RtioTcpAcceptor for TcpAcceptor {
-    fn accept(&mut self) -> IoResult<Box<rtio::RtioTcpStream + Send>> {
-        self.native_accept().map(|s| box s as Box<rtio::RtioTcpStream + Send>)
-    }
-
-    fn accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) }
-    fn dont_accept_simultaneously(&mut self) -> IoResult<()> { Ok(()) }
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        self.deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
-    }
-
-    fn clone(&self) -> Box<rtio::RtioTcpAcceptor + Send> {
-        box TcpAcceptor {
+impl Clone for TcpStream {
+    fn clone(&self) -> TcpStream {
+        TcpStream {
             inner: self.inner.clone(),
-            deadline: 0,
-        } as Box<rtio::RtioTcpAcceptor + Send>
-    }
-
-    #[cfg(unix)]
-    fn close_accept(&mut self) -> IoResult<()> {
-        self.inner.closed.store(true, atomic::SeqCst);
-        let mut fd = FileDesc::new(self.inner.writer.fd(), false);
-        match fd.inner_write([0]) {
-            Ok(..) => Ok(()),
-            Err(..) if util::wouldblock() => Ok(()),
-            Err(e) => Err(e),
-        }
-    }
-
-    #[cfg(windows)]
-    fn close_accept(&mut self) -> IoResult<()> {
-        self.inner.closed.store(true, atomic::SeqCst);
-        let ret = unsafe { c::WSASetEvent(self.inner.abort.handle()) };
-        if ret == libc::TRUE {
-            Ok(())
-        } else {
-            Err(os::last_error())
+            read_deadline: 0,
+            write_deadline: 0,
         }
     }
 }
@@ -690,7 +740,9 @@ pub struct UdpSocket {
 }
 
 impl UdpSocket {
-    pub fn bind(addr: rtio::SocketAddr) -> IoResult<UdpSocket> {
+    pub fn bind(addr: SocketAddr) -> IoResult<UdpSocket> {
+        sys::init_net();
+
         let fd = try!(socket(addr, libc::SOCK_DGRAM));
         let ret = UdpSocket {
             inner: Arc::new(Inner::new(fd)),
@@ -703,7 +755,7 @@ impl UdpSocket {
         let addrp = &storage as *const _ as *const libc::sockaddr;
 
         match unsafe { libc::bind(fd, addrp, len) } {
-            -1 => Err(os::last_error()),
+            -1 => Err(last_error()),
             _ => Ok(ret),
         }
     }
@@ -720,8 +772,7 @@ impl UdpSocket {
                    on as libc::c_int)
     }
 
-    pub fn set_membership(&mut self, addr: rtio::IpAddr,
-                          opt: libc::c_int) -> IoResult<()> {
+    pub fn set_membership(&mut self, addr: IpAddr, opt: libc::c_int) -> IoResult<()> {
         match ip_to_inaddr(addr) {
             In4Addr(addr) => {
                 let mreq = libc::ip_mreq {
@@ -750,22 +801,15 @@ impl UdpSocket {
             fd: self.fd(),
             guard: unsafe { self.inner.lock.lock() },
         };
-        assert!(util::set_nonblocking(self.fd(), true).is_ok());
+        assert!(set_nonblocking(self.fd(), true).is_ok());
         ret
     }
-}
 
-impl rtio::RtioSocket for UdpSocket {
-    fn socket_name(&mut self) -> IoResult<rtio::SocketAddr> {
+    pub fn socket_name(&mut self) -> IoResult<SocketAddr> {
         sockname(self.fd(), libc::getsockname)
     }
-}
-
-#[cfg(windows)] type msglen_t = libc::c_int;
-#[cfg(unix)]    type msglen_t = libc::size_t;
 
-impl rtio::RtioUdpSocket for UdpSocket {
-    fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, rtio::SocketAddr)> {
+    pub fn recv_from(&mut self, buf: &mut [u8]) -> IoResult<(uint, SocketAddr)> {
         let fd = self.fd();
         let mut storage: libc::sockaddr_storage = unsafe { mem::zeroed() };
         let storagep = &mut storage as *mut _ as *mut libc::sockaddr;
@@ -787,7 +831,7 @@ impl rtio::RtioUdpSocket for UdpSocket {
         })
     }
 
-    fn send_to(&mut self, buf: &[u8], dst: rtio::SocketAddr) -> IoResult<()> {
+    pub fn send_to(&mut self, buf: &[u8], dst: SocketAddr) -> IoResult<()> {
         let mut storage = unsafe { mem::zeroed() };
         let dstlen = addr_to_sockaddr(dst, &mut storage);
         let dstp = &storage as *const _ as *const libc::sockaddr;
@@ -806,298 +850,60 @@ impl rtio::RtioUdpSocket for UdpSocket {
 
         let n = try!(write(fd, self.write_deadline, buf, false, dolock, dowrite));
         if n != buf.len() {
-            Err(util::short_write(n, "couldn't send entire packet at once"))
+            Err(short_write(n, "couldn't send entire packet at once"))
         } else {
             Ok(())
         }
     }
 
-    fn join_multicast(&mut self, multi: rtio::IpAddr) -> IoResult<()> {
+    pub fn join_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
         match multi {
-            rtio::Ipv4Addr(..) => {
+            Ipv4Addr(..) => {
                 self.set_membership(multi, libc::IP_ADD_MEMBERSHIP)
             }
-            rtio::Ipv6Addr(..) => {
+            Ipv6Addr(..) => {
                 self.set_membership(multi, libc::IPV6_ADD_MEMBERSHIP)
             }
         }
     }
-    fn leave_multicast(&mut self, multi: rtio::IpAddr) -> IoResult<()> {
+    pub fn leave_multicast(&mut self, multi: IpAddr) -> IoResult<()> {
         match multi {
-            rtio::Ipv4Addr(..) => {
+            Ipv4Addr(..) => {
                 self.set_membership(multi, libc::IP_DROP_MEMBERSHIP)
             }
-            rtio::Ipv6Addr(..) => {
+            Ipv6Addr(..) => {
                 self.set_membership(multi, libc::IPV6_DROP_MEMBERSHIP)
             }
         }
     }
 
-    fn loop_multicast_locally(&mut self) -> IoResult<()> {
-        self.set_multicast_loop(true)
-    }
-    fn dont_loop_multicast_locally(&mut self) -> IoResult<()> {
-        self.set_multicast_loop(false)
-    }
-
-    fn multicast_time_to_live(&mut self, ttl: int) -> IoResult<()> {
+    pub fn multicast_time_to_live(&mut self, ttl: int) -> IoResult<()> {
         setsockopt(self.fd(), libc::IPPROTO_IP, libc::IP_MULTICAST_TTL,
                    ttl as libc::c_int)
     }
-    fn time_to_live(&mut self, ttl: int) -> IoResult<()> {
+    pub fn time_to_live(&mut self, ttl: int) -> IoResult<()> {
         setsockopt(self.fd(), libc::IPPROTO_IP, libc::IP_TTL, ttl as libc::c_int)
     }
 
-    fn hear_broadcasts(&mut self) -> IoResult<()> {
-        self.set_broadcast(true)
-    }
-    fn ignore_broadcasts(&mut self) -> IoResult<()> {
-        self.set_broadcast(false)
-    }
-
-    fn clone(&self) -> Box<rtio::RtioUdpSocket + Send> {
-        box UdpSocket {
-            inner: self.inner.clone(),
-            read_deadline: 0,
-            write_deadline: 0,
-        } as Box<rtio::RtioUdpSocket + Send>
-    }
-
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
         self.read_deadline = deadline;
         self.write_deadline = deadline;
     }
-    fn set_read_timeout(&mut self, timeout: Option<u64>) {
-        self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
+        self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
-    fn set_write_timeout(&mut self, timeout: Option<u64>) {
-        self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
+        self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
 }
 
-////////////////////////////////////////////////////////////////////////////////
-// Timeout helpers
-//
-// The read/write functions below are the helpers for reading/writing a socket
-// with a possible deadline specified. This is generally viewed as a timed out
-// I/O operation.
-//
-// From the application's perspective, timeouts apply to the I/O object, not to
-// the underlying file descriptor (it's one timeout per object). This means that
-// we can't use the SO_RCVTIMEO and corresponding send timeout option.
-//
-// The next idea to implement timeouts would be to use nonblocking I/O. An
-// invocation of select() would wait (with a timeout) for a socket to be ready.
-// Once its ready, we can perform the operation. Note that the operation *must*
-// be nonblocking, even though select() says the socket is ready. This is
-// because some other thread could have come and stolen our data (handles can be
-// cloned).
-//
-// To implement nonblocking I/O, the first option we have is to use the
-// O_NONBLOCK flag. Remember though that this is a global setting, affecting all
-// I/O objects, so this was initially viewed as unwise.
-//
-// It turns out that there's this nifty MSG_DONTWAIT flag which can be passed to
-// send/recv, but the niftiness wears off once you realize it only works well on
-// Linux [1] [2]. This means that it's pretty easy to get a nonblocking
-// operation on Linux (no flag fiddling, no affecting other objects), but not on
-// other platforms.
-//
-// To work around this constraint on other platforms, we end up using the
-// original strategy of flipping the O_NONBLOCK flag. As mentioned before, this
-// could cause other objects' blocking operations to suddenly become
-// nonblocking. To get around this, a "blocking operation" which returns EAGAIN
-// falls back to using the same code path as nonblocking operations, but with an
-// infinite timeout (select + send/recv). This helps emulate blocking
-// reads/writes despite the underlying descriptor being nonblocking, as well as
-// optimizing the fast path of just hitting one syscall in the good case.
-//
-// As a final caveat, this implementation uses a mutex so only one thread is
-// doing a nonblocking operation at at time. This is the operation that comes
-// after the select() (at which point we think the socket is ready). This is
-// done for sanity to ensure that the state of the O_NONBLOCK flag is what we
-// expect (wouldn't want someone turning it on when it should be off!). All
-// operations performed in the lock are *nonblocking* to avoid holding the mutex
-// forever.
-//
-// So, in summary, Linux uses MSG_DONTWAIT and doesn't need mutexes, everyone
-// else uses O_NONBLOCK and mutexes with some trickery to make sure blocking
-// reads/writes are still blocking.
-//
-// Fun, fun!
-//
-// [1] http://twistedmatrix.com/pipermail/twisted-commits/2012-April/034692.html
-// [2] http://stackoverflow.com/questions/19819198/does-send-msg-dontwait
-
-pub fn read<T>(fd: sock_t,
-               deadline: u64,
-               lock: || -> T,
-               read: |bool| -> libc::c_int) -> IoResult<uint> {
-    let mut ret = -1;
-    if deadline == 0 {
-        ret = retry(|| read(false));
-    }
-
-    if deadline != 0 || (ret == -1 && util::wouldblock()) {
-        let deadline = match deadline {
-            0 => None,
-            n => Some(n),
-        };
-        loop {
-            // With a timeout, first we wait for the socket to become
-            // readable using select(), specifying the relevant timeout for
-            // our previously set deadline.
-            try!(util::await([fd], deadline, util::Readable));
-
-            // At this point, we're still within the timeout, and we've
-            // determined that the socket is readable (as returned by
-            // select). We must still read the socket in *nonblocking* mode
-            // because some other thread could come steal our data. If we
-            // fail to read some data, we retry (hence the outer loop) and
-            // wait for the socket to become readable again.
-            let _guard = lock();
-            match retry(|| read(deadline.is_some())) {
-                -1 if util::wouldblock() => {}
-                -1 => return Err(os::last_error()),
-               n => { ret = n; break }
-            }
-        }
-    }
-
-    match ret {
-        0 => Err(util::eof()),
-        n if n < 0 => Err(os::last_error()),
-        n => Ok(n as uint)
-    }
-}
-
-pub fn write<T>(fd: sock_t,
-                deadline: u64,
-                buf: &[u8],
-                write_everything: bool,
-                lock: || -> T,
-                write: |bool, *const u8, uint| -> i64) -> IoResult<uint> {
-    let mut ret = -1;
-    let mut written = 0;
-    if deadline == 0 {
-        if write_everything {
-            ret = keep_going(buf, |inner, len| {
-                written = buf.len() - len;
-                write(false, inner, len)
-            });
-        } else {
-            ret = retry(|| { write(false, buf.as_ptr(), buf.len()) });
-            if ret > 0 { written = ret as uint; }
-        }
-    }
-
-    if deadline != 0 || (ret == -1 && util::wouldblock()) {
-        let deadline = match deadline {
-            0 => None,
-            n => Some(n),
-        };
-        while written < buf.len() && (write_everything || written == 0) {
-            // As with read(), first wait for the socket to be ready for
-            // the I/O operation.
-            match util::await([fd], deadline, util::Writable) {
-                Err(ref e) if e.code == libc::EOF as uint && written > 0 => {
-                    assert!(deadline.is_some());
-                    return Err(util::short_write(written, "short write"))
-                }
-                Err(e) => return Err(e),
-                Ok(()) => {}
-            }
-
-            // Also as with read(), we use MSG_DONTWAIT to guard ourselves
-            // against unforeseen circumstances.
-            let _guard = lock();
-            let ptr = buf[written..].as_ptr();
-            let len = buf.len() - written;
-            match retry(|| write(deadline.is_some(), ptr, len)) {
-                -1 if util::wouldblock() => {}
-                -1 => return Err(os::last_error()),
-                n => { written += n as uint; }
-            }
-        }
-        ret = 0;
-    }
-    if ret < 0 {
-        Err(os::last_error())
-    } else {
-        Ok(written)
-    }
-}
-
-#[cfg(windows)]
-mod os {
-    use libc;
-    use std::mem;
-    use std::rt::rtio::{IoError, IoResult};
-
-    use io::c;
-
-    pub type sock_t = libc::SOCKET;
-    pub struct Event(c::WSAEVENT);
-
-    impl Event {
-        pub fn new() -> IoResult<Event> {
-            let event = unsafe { c::WSACreateEvent() };
-            if event == c::WSA_INVALID_EVENT {
-                Err(last_error())
-            } else {
-                Ok(Event(event))
-            }
-        }
-
-        pub fn handle(&self) -> c::WSAEVENT { let Event(handle) = *self; handle }
-    }
-
-    impl Drop for Event {
-        fn drop(&mut self) {
-            unsafe { let _ = c::WSACloseEvent(self.handle()); }
-        }
-    }
-
-    pub fn init() {
-        unsafe {
-            use std::rt::mutex::{StaticNativeMutex, NATIVE_MUTEX_INIT};
-            static mut INITIALIZED: bool = false;
-            static LOCK: StaticNativeMutex = NATIVE_MUTEX_INIT;
-
-            let _guard = LOCK.lock();
-            if !INITIALIZED {
-                let mut data: c::WSADATA = mem::zeroed();
-                let ret = c::WSAStartup(0x202,      // version 2.2
-                                        &mut data);
-                assert_eq!(ret, 0);
-                INITIALIZED = true;
-            }
-        }
-    }
-
-    pub fn last_error() -> IoError {
-        use std::os;
-        let code = unsafe { c::WSAGetLastError() as uint };
-        IoError {
-            code: code,
-            extra: 0,
-            detail: Some(os::error_string(code)),
+impl Clone for UdpSocket {
+    fn clone(&self) -> UdpSocket {
+        UdpSocket {
+            inner: self.inner.clone(),
+            read_deadline: 0,
+            write_deadline: 0,
         }
     }
-
-    pub unsafe fn close(sock: sock_t) { let _ = libc::closesocket(sock); }
-}
-
-#[cfg(unix)]
-mod os {
-    use libc;
-    use std::rt::rtio::IoError;
-    use io;
-
-    pub type sock_t = io::file::fd_t;
-
-    pub fn init() {}
-    pub fn last_error() -> IoError { io::last_error() }
-    pub unsafe fn close(sock: sock_t) { let _ = libc::close(sock); }
 }
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index ad5de2dad480d..5a43fd08f9047 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -8,24 +8,51 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#![allow(missing_doc)]
+
 extern crate libc;
 
 use num;
 use prelude::*;
 use io::{mod, IoResult, IoError};
+use sys_common::mkerr_libc;
 
+pub mod c;
 pub mod fs;
 pub mod os;
-pub mod c;
+pub mod tcp;
+pub mod udp;
+pub mod pipe;
+
+pub mod addrinfo {
+    pub use sys_common::net::get_host_addresses;
+}
 
-pub type sock_t = io::file::fd_t;
+// FIXME: move these to c module
+pub type sock_t = self::fs::fd_t;
 pub type wrlen = libc::size_t;
+pub type msglen_t = libc::size_t;
 pub unsafe fn close_sock(sock: sock_t) { let _ = libc::close(sock); }
 
 pub fn last_error() -> IoError {
-    let errno = os::errno() as i32;
-    let mut err = decode_error(errno);
-    err.detail = Some(os::error_string(errno));
+    decode_error_detailed(os::errno() as i32)
+}
+
+pub fn last_net_error() -> IoError {
+    last_error()
+}
+
+extern "system" {
+    fn gai_strerror(errcode: libc::c_int) -> *const libc::c_char;
+}
+
+pub fn last_gai_error(s: libc::c_int) -> IoError {
+    use c_str::CString;
+
+    let mut err = decode_error(s);
+    err.detail = Some(unsafe {
+        CString::new(gai_strerror(s), false).as_str().unwrap().to_string()
+    });
     err
 }
 
@@ -64,6 +91,12 @@ pub fn decode_error(errno: i32) -> IoError {
     IoError { kind: kind, desc: desc, detail: None }
 }
 
+pub fn decode_error_detailed(errno: i32) -> IoError {
+    let mut err = decode_error(errno);
+    err.detail = Some(os::error_string(errno));
+    err
+}
+
 #[inline]
 pub fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
     let minus_one = -num::one::<I>();
@@ -86,7 +119,10 @@ pub fn wouldblock() -> bool {
     err == libc::EWOULDBLOCK as int || err == libc::EAGAIN as int
 }
 
-pub fn set_nonblocking(fd: net::sock_t, nb: bool) -> IoResult<()> {
+pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
     let set = nb as libc::c_int;
-    super::mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
+    mkerr_libc(retry(|| unsafe { c::ioctl(fd, c::FIONBIO, &set) }))
 }
+
+// nothing needed on unix platforms
+pub fn init_net() {}
diff --git a/src/libstd/sys/unix/os.rs b/src/libstd/sys/unix/os.rs
index 34699eb27c115..4e495f043bc63 100644
--- a/src/libstd/sys/unix/os.rs
+++ b/src/libstd/sys/unix/os.rs
@@ -11,6 +11,8 @@
 use libc;
 use libc::{c_int, c_char};
 use prelude::*;
+use io::IoResult;
+use sys::fs::FileDesc;
 
 use os::TMPBUF_SZ;
 
@@ -99,3 +101,12 @@ pub fn error_string(errno: i32) -> String {
         ::string::raw::from_buf(p as *const u8)
     }
 }
+
+pub unsafe fn pipe() -> IoResult<(FileDesc, FileDesc)> {
+    let mut fds = [0, ..2];
+    if libc::pipe(fds.as_mut_ptr()) == 0 {
+        Ok((FileDesc::new(fds[0], true), FileDesc::new(fds[1], true)))
+    } else {
+        Err(super::last_error())
+    }
+}
diff --git a/src/libnative/io/pipe_unix.rs b/src/libstd/sys/unix/pipe.rs
similarity index 67%
rename from src/libnative/io/pipe_unix.rs
rename to src/libstd/sys/unix/pipe.rs
index 48f31615339a0..67384848a9449 100644
--- a/src/libnative/io/pipe_unix.rs
+++ b/src/libstd/sys/unix/pipe.rs
@@ -10,19 +10,17 @@
 
 use alloc::arc::Arc;
 use libc;
-use std::c_str::CString;
-use std::mem;
-use std::rt::mutex;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::sync::atomic;
-
-use super::retry;
-use super::net;
-use super::util;
-use super::c;
-use super::process;
-use super::file::{fd_t, FileDesc};
+use c_str::CString;
+use mem;
+use rt::mutex;
+use sync::atomic;
+use io::{mod, IoResult, IoError};
+use prelude::*;
+
+use sys::{mod, timer, retry, c, set_nonblocking, wouldblock};
+use sys::fs::{fd_t, FileDesc};
+use sys_common::net::*;
+use sys_common::{eof, mkerr_libc};
 
 fn unix_socket(ty: libc::c_int) -> IoResult<fd_t> {
     match unsafe { libc::socket(libc::AF_UNIX, ty, 0) } {
@@ -41,12 +39,10 @@ fn addr_to_sockaddr_un(addr: &CString,
 
     let len = addr.len();
     if len > s.sun_path.len() - 1 {
-        #[cfg(unix)] use libc::EINVAL as ERROR;
-        #[cfg(windows)] use libc::WSAEINVAL as ERROR;
         return Err(IoError {
-            code: ERROR as uint,
-            extra: 0,
-            detail: Some("path must be smaller than SUN_LEN".to_string()),
+            kind: io::InvalidInput,
+            desc: "invalid argument: path must be smaller than SUN_LEN",
+            detail: None,
         })
     }
     s.sun_family = libc::AF_UNIX as libc::sa_family_t;
@@ -92,7 +88,7 @@ fn connect(addr: &CString, ty: libc::c_int,
             }
         }
         Some(timeout_ms) => {
-            try!(util::connect_timeout(inner.fd, addrp, len, timeout_ms));
+            try!(connect_timeout(inner.fd, addrp, len, timeout_ms));
             Ok(inner)
         }
     }
@@ -143,18 +139,16 @@ impl UnixStream {
     fn lock_nonblocking(&self) {}
 
     #[cfg(not(target_os = "linux"))]
-    fn lock_nonblocking<'a>(&'a self) -> net::Guard<'a> {
-        let ret = net::Guard {
+    fn lock_nonblocking<'a>(&'a self) -> Guard<'a> {
+        let ret = Guard {
             fd: self.fd(),
             guard: unsafe { self.inner.lock.lock() },
         };
-        assert!(util::set_nonblocking(self.fd(), true).is_ok());
+        assert!(set_nonblocking(self.fd(), true).is_ok());
         ret
     }
-}
 
-impl rtio::RtioPipe for UnixStream {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
         let fd = self.fd();
         let dolock = || self.lock_nonblocking();
         let doread = |nb| unsafe {
@@ -164,10 +158,10 @@ impl rtio::RtioPipe for UnixStream {
                        buf.len() as libc::size_t,
                        flags) as libc::c_int
         };
-        net::read(fd, self.read_deadline, dolock, doread)
+        read(fd, self.read_deadline, dolock, doread)
     }
 
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         let fd = self.fd();
         let dolock = || self.lock_nonblocking();
         let dowrite = |nb: bool, buf: *const u8, len: uint| unsafe {
@@ -177,32 +171,38 @@ impl rtio::RtioPipe for UnixStream {
                        len as libc::size_t,
                        flags) as i64
         };
-        match net::write(fd, self.write_deadline, buf, true, dolock, dowrite) {
+        match write(fd, self.write_deadline, buf, true, dolock, dowrite) {
             Ok(_) => Ok(()),
             Err(e) => Err(e)
         }
     }
 
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box UnixStream::new(self.inner.clone()) as Box<rtio::RtioPipe + Send>
+    pub fn close_write(&mut self) -> IoResult<()> {
+        mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
     }
 
-    fn close_write(&mut self) -> IoResult<()> {
-        super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_WR) })
-    }
-    fn close_read(&mut self) -> IoResult<()> {
-        super::mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
+    pub fn close_read(&mut self) -> IoResult<()> {
+        mkerr_libc(unsafe { libc::shutdown(self.fd(), libc::SHUT_RD) })
     }
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
         self.read_deadline = deadline;
         self.write_deadline = deadline;
     }
-    fn set_read_timeout(&mut self, timeout: Option<u64>) {
-        self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+
+    pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
+        self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
+    }
+
+    pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
+        self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
-    fn set_write_timeout(&mut self, timeout: Option<u64>) {
-        self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+}
+
+impl Clone for UnixStream {
+    fn clone(&self) -> UnixStream {
+        UnixStream::new(self.inner.clone())
     }
 }
 
@@ -224,16 +224,15 @@ impl UnixListener {
 
     fn fd(&self) -> fd_t { self.inner.fd }
 
-    pub fn native_listen(self, backlog: int) -> IoResult<UnixAcceptor> {
-        match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
+    pub fn listen(self) -> IoResult<UnixAcceptor> {
+        match unsafe { libc::listen(self.fd(), 128) } {
             -1 => Err(super::last_error()),
 
-            #[cfg(unix)]
             _ => {
-                let (reader, writer) = try!(process::pipe());
-                try!(util::set_nonblocking(reader.fd(), true));
-                try!(util::set_nonblocking(writer.fd(), true));
-                try!(util::set_nonblocking(self.fd(), true));
+                let (reader, writer) = try!(unsafe { sys::os::pipe() });
+                try!(set_nonblocking(reader.fd(), true));
+                try!(set_nonblocking(writer.fd(), true));
+                try!(set_nonblocking(self.fd(), true));
                 Ok(UnixAcceptor {
                     inner: Arc::new(AcceptorInner {
                         listener: self,
@@ -248,21 +247,11 @@ impl UnixListener {
     }
 }
 
-impl rtio::RtioUnixListener for UnixListener {
-    fn listen(self: Box<UnixListener>)
-              -> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
-        self.native_listen(128).map(|a| {
-            box a as Box<rtio::RtioUnixAcceptor + Send>
-        })
-    }
-}
-
 pub struct UnixAcceptor {
     inner: Arc<AcceptorInner>,
     deadline: u64,
 }
 
-#[cfg(unix)]
 struct AcceptorInner {
     listener: UnixListener,
     reader: FileDesc,
@@ -273,7 +262,7 @@ struct AcceptorInner {
 impl UnixAcceptor {
     fn fd(&self) -> fd_t { self.inner.listener.fd() }
 
-    pub fn native_accept(&mut self) -> IoResult<UnixStream> {
+    pub fn accept(&mut self) -> IoResult<UnixStream> {
         let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
 
         while !self.inner.closed.load(atomic::SeqCst) {
@@ -287,46 +276,39 @@ impl UnixAcceptor {
                                  storagep as *mut libc::sockaddr,
                                  &mut size as *mut libc::socklen_t) as libc::c_int
                 }) {
-                    -1 if util::wouldblock() => {}
+                    -1 if wouldblock() => {}
                     -1 => return Err(super::last_error()),
                     fd => return Ok(UnixStream::new(Arc::new(Inner::new(fd)))),
                 }
             }
-            try!(util::await([self.fd(), self.inner.reader.fd()],
-                             deadline, util::Readable));
+            try!(await([self.fd(), self.inner.reader.fd()],
+                             deadline, Readable));
         }
 
-        Err(util::eof())
+        Err(eof())
     }
-}
 
-impl rtio::RtioUnixAcceptor for UnixAcceptor {
-    fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
-        self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
-    }
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        self.deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
 
-    fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> {
-        box UnixAcceptor {
-            inner: self.inner.clone(),
-            deadline: 0,
-        } as Box<rtio::RtioUnixAcceptor + Send>
-    }
-
-    #[cfg(unix)]
-    fn close_accept(&mut self) -> IoResult<()> {
+    pub fn close_accept(&mut self) -> IoResult<()> {
         self.inner.closed.store(true, atomic::SeqCst);
-        let mut fd = FileDesc::new(self.inner.writer.fd(), false);
-        match fd.inner_write([0]) {
+        let fd = FileDesc::new(self.inner.writer.fd(), false);
+        match fd.write([0]) {
             Ok(..) => Ok(()),
-            Err(..) if util::wouldblock() => Ok(()),
+            Err(..) if wouldblock() => Ok(()),
             Err(e) => Err(e),
         }
     }
 }
 
+impl Clone for UnixAcceptor {
+    fn clone(&self) -> UnixAcceptor {
+        UnixAcceptor { inner: self.inner.clone(), deadline: 0 }
+    }
+}
+
 impl Drop for UnixListener {
     fn drop(&mut self) {
         // Unlink the path to the socket to ensure that it doesn't linger. We're
diff --git a/src/libstd/sys/unix/tcp.rs b/src/libstd/sys/unix/tcp.rs
new file mode 100644
index 0000000000000..962475e417719
--- /dev/null
+++ b/src/libstd/sys/unix/tcp.rs
@@ -0,0 +1,157 @@
+// Copyright 2014 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 io::net::ip;
+use io::IoResult;
+use libc;
+use mem;
+use ptr;
+use prelude::*;
+use super::{last_error, last_net_error, retry, sock_t};
+use sync::{Arc, atomic};
+use sys::fs::FileDesc;
+use sys::{set_nonblocking, wouldblock};
+use sys;
+use sys_common;
+use sys_common::net::*;
+
+pub use sys_common::net::TcpStream;
+
+////////////////////////////////////////////////////////////////////////////////
+// TCP listeners
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct TcpListener {
+    pub inner: FileDesc,
+}
+
+impl TcpListener {
+    pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
+        let fd = try!(socket(addr, libc::SOCK_STREAM));
+        let ret = TcpListener { inner: FileDesc::new(fd, true) };
+
+        let mut storage = unsafe { mem::zeroed() };
+        let len = addr_to_sockaddr(addr, &mut storage);
+        let addrp = &storage as *const _ as *const libc::sockaddr;
+
+        // On platforms with Berkeley-derived sockets, this allows
+        // to quickly rebind a socket, without needing to wait for
+        // the OS to clean up the previous one.
+        try!(setsockopt(fd, libc::SOL_SOCKET, libc::SO_REUSEADDR, 1 as libc::c_int));
+
+
+        match unsafe { libc::bind(fd, addrp, len) } {
+            -1 => Err(last_error()),
+            _ => Ok(ret),
+        }
+    }
+
+    pub fn fd(&self) -> sock_t { self.inner.fd() }
+
+    pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> {
+        match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
+            -1 => Err(last_net_error()),
+            _ => {
+                let (reader, writer) = try!(unsafe { sys::os::pipe() });
+                try!(set_nonblocking(reader.fd(), true));
+                try!(set_nonblocking(writer.fd(), true));
+                try!(set_nonblocking(self.fd(), true));
+                Ok(TcpAcceptor {
+                    inner: Arc::new(AcceptorInner {
+                        listener: self,
+                        reader: reader,
+                        writer: writer,
+                        closed: atomic::AtomicBool::new(false),
+                    }),
+                    deadline: 0,
+                })
+            }
+        }
+    }
+
+    pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
+        sockname(self.fd(), libc::getsockname)
+    }
+}
+
+pub struct TcpAcceptor {
+    inner: Arc<AcceptorInner>,
+    deadline: u64,
+}
+
+struct AcceptorInner {
+    listener: TcpListener,
+    reader: FileDesc,
+    writer: FileDesc,
+    closed: atomic::AtomicBool,
+}
+
+impl TcpAcceptor {
+    pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
+
+    pub fn accept(&mut self) -> IoResult<TcpStream> {
+        // In implementing accept, the two main concerns are dealing with
+        // close_accept() and timeouts. The unix implementation is based on a
+        // nonblocking accept plus a call to select(). Windows ends up having
+        // an entirely separate implementation than unix, which is explained
+        // below.
+        //
+        // To implement timeouts, all blocking is done via select() instead of
+        // accept() by putting the socket in non-blocking mode. Because
+        // select() takes a timeout argument, we just pass through the timeout
+        // to select().
+        //
+        // To implement close_accept(), we have a self-pipe to ourselves which
+        // is passed to select() along with the socket being accepted on. The
+        // self-pipe is never written to unless close_accept() is called.
+        let deadline = if self.deadline == 0 {None} else {Some(self.deadline)};
+
+        while !self.inner.closed.load(atomic::SeqCst) {
+            match retry(|| unsafe {
+                libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
+            }) {
+                -1 if wouldblock() => {}
+                -1 => return Err(last_net_error()),
+                fd => return Ok(TcpStream::new(fd as sock_t)),
+            }
+            try!(await([self.fd(), self.inner.reader.fd()],
+                             deadline, Readable));
+        }
+
+        Err(sys_common::eof())
+    }
+
+    pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
+        sockname(self.fd(), libc::getsockname)
+    }
+
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        self.deadline = timeout.map(|a| sys::timer::now() + a).unwrap_or(0);
+    }
+
+    pub fn close_accept(&mut self) -> IoResult<()> {
+        self.inner.closed.store(true, atomic::SeqCst);
+        let fd = FileDesc::new(self.inner.writer.fd(), false);
+        match fd.write([0]) {
+            Ok(..) => Ok(()),
+            Err(..) if wouldblock() => Ok(()),
+            Err(e) => Err(e),
+        }
+    }
+}
+
+impl Clone for TcpAcceptor {
+    fn clone(&self) -> TcpAcceptor {
+        TcpAcceptor {
+            inner: self.inner.clone(),
+            deadline: 0,
+        }
+    }
+}
diff --git a/src/libstd/sys/unix/udp.rs b/src/libstd/sys/unix/udp.rs
new file mode 100644
index 0000000000000..50f8fb828ad32
--- /dev/null
+++ b/src/libstd/sys/unix/udp.rs
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+pub use sys_common::net::UdpSocket;
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 5f4129c14842a..85fbc6b936c30 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -33,12 +33,21 @@ macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
     };
 ) )
 
+pub mod c;
 pub mod fs;
 pub mod os;
-pub mod c;
+pub mod tcp;
+pub mod udp;
+pub mod pipe;
+
+pub mod addrinfo {
+    pub use sys_common::net::get_host_addresses;
+}
 
+// FIXME: move these to c module
 pub type sock_t = libc::SOCKET;
 pub type wrlen = libc::c_int;
+pub type msglen_t = libc::c_int;
 pub unsafe fn close_sock(sock: sock_t) { let _ = libc::closesocket(sock); }
 
 // windows has zero values as errors
@@ -140,7 +149,6 @@ pub fn set_nonblocking(fd: sock_t, nb: bool) -> IoResult<()> {
     }
 }
 
-// FIXME: call this
 pub fn init_net() {
     unsafe {
         static START: Once = ONCE_INIT;
diff --git a/src/libnative/io/pipe_windows.rs b/src/libstd/sys/windows/pipe.rs
similarity index 89%
rename from src/libnative/io/pipe_windows.rs
rename to src/libstd/sys/windows/pipe.rs
index f764470f37d89..f2f7994a0057c 100644
--- a/src/libnative/io/pipe_windows.rs
+++ b/src/libstd/sys/windows/pipe.rs
@@ -86,18 +86,17 @@
 
 use alloc::arc::Arc;
 use libc;
-use std::c_str::CString;
-use std::mem;
-use std::os;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, IoError};
-use std::sync::atomic;
-use std::rt::mutex;
-
-use super::c;
-use super::util;
-use super::file::to_utf16;
+use c_str::CString;
+use mem;
+use ptr;
+use sync::atomic;
+use rt::mutex;
+use io::{mod, IoError, IoResult};
+use prelude::*;
+
+use sys_common::{mod, eof};
+
+use super::{c, os, timer, to_utf16, decode_error_detailed};
 
 struct Event(libc::HANDLE);
 
@@ -177,7 +176,7 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
     let ms = if deadline == 0 {
         libc::INFINITE as u64
     } else {
-        let now = ::io::timer::now();
+        let now = timer::now();
         if deadline < now {0} else {deadline - now}
     };
     let ret = unsafe {
@@ -190,7 +189,7 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
         WAIT_FAILED => Err(super::last_error()),
         WAIT_TIMEOUT => unsafe {
             let _ = c::CancelIo(handle);
-            Err(util::timeout("operation timed out"))
+            Err(sys_common::timeout("operation timed out"))
         },
         n => Ok((n - WAIT_OBJECT_0) as uint)
     }
@@ -198,8 +197,8 @@ pub fn await(handle: libc::HANDLE, deadline: u64,
 
 fn epipe() -> IoError {
     IoError {
-        code: libc::ERROR_BROKEN_PIPE as uint,
-        extra: 0,
+        kind: io::EndOfFile,
+        desc: "the pipe has ended",
         detail: None,
     }
 }
@@ -268,8 +267,8 @@ impl UnixStream {
     }
 
     pub fn connect(addr: &CString, timeout: Option<u64>) -> IoResult<UnixStream> {
-        let addr = try!(to_utf16(addr));
-        let start = ::io::timer::now();
+        let addr = try!(to_utf16(addr.as_str()));
+        let start = timer::now();
         loop {
             match UnixStream::try_connect(addr.as_ptr()) {
                 Some(handle) => {
@@ -308,13 +307,13 @@ impl UnixStream {
 
             match timeout {
                 Some(timeout) => {
-                    let now = ::io::timer::now();
+                    let now = timer::now();
                     let timed_out = (now - start) >= timeout || unsafe {
                         let ms = (timeout - (now - start)) as libc::DWORD;
                         libc::WaitNamedPipeW(addr.as_ptr(), ms) == 0
                     };
                     if timed_out {
-                        return Err(util::timeout("connect timed out"))
+                        return Err(sys_common::timeout("connect timed out"))
                     }
                 }
 
@@ -349,10 +348,8 @@ impl UnixStream {
             _ => Ok(())
         }
     }
-}
 
-impl rtio::RtioPipe for UnixStream {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
         if self.read.is_none() {
             self.read = Some(try!(Event::new(true, false)));
         }
@@ -368,7 +365,7 @@ impl rtio::RtioPipe for UnixStream {
         // See comments in close_read() about why this lock is necessary.
         let guard = unsafe { self.inner.lock.lock() };
         if self.read_closed() {
-            return Err(util::eof())
+            return Err(eof())
         }
 
         // Issue a nonblocking requests, succeeding quickly if it happened to
@@ -416,15 +413,15 @@ impl rtio::RtioPipe for UnixStream {
             // If the reading half is now closed, then we're done. If we woke up
             // because the writing half was closed, keep trying.
             if wait_succeeded.is_err() {
-                return Err(util::timeout("read timed out"))
+                return Err(sys_common::timeout("read timed out"))
             }
             if self.read_closed() {
-                return Err(util::eof())
+                return Err(eof())
             }
         }
     }
 
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         if self.write.is_none() {
             self.write = Some(try!(Event::new(true, false)));
         }
@@ -458,11 +455,7 @@ impl rtio::RtioPipe for UnixStream {
 
             if ret == 0 {
                 if err != libc::ERROR_IO_PENDING as uint {
-                    return Err(IoError {
-                        code: err as uint,
-                        extra: 0,
-                        detail: Some(os::error_string(err as uint)),
-                    })
+                    return Err(decode_error_detailed(err as i32))
                 }
                 // Process a timeout if one is pending
                 let wait_succeeded = await(self.handle(), self.write_deadline,
@@ -484,12 +477,12 @@ impl rtio::RtioPipe for UnixStream {
                         let amt = offset + bytes_written as uint;
                         return if amt > 0 {
                             Err(IoError {
-                                code: libc::ERROR_OPERATION_ABORTED as uint,
-                                extra: amt,
-                                detail: Some("short write during write".to_string()),
+                                kind: io::ShortWrite(amt),
+                                desc: "short write during write",
+                                detail: None,
                             })
                         } else {
-                            Err(util::timeout("write timed out"))
+                            Err(sys_common::timeout("write timed out"))
                         }
                     }
                     if self.write_closed() {
@@ -503,17 +496,7 @@ impl rtio::RtioPipe for UnixStream {
         Ok(())
     }
 
-    fn clone(&self) -> Box<rtio::RtioPipe + Send> {
-        box UnixStream {
-            inner: self.inner.clone(),
-            read: None,
-            write: None,
-            read_deadline: 0,
-            write_deadline: 0,
-        } as Box<rtio::RtioPipe + Send>
-    }
-
-    fn close_read(&mut self) -> IoResult<()> {
+    pub fn close_read(&mut self) -> IoResult<()> {
         // On windows, there's no actual shutdown() method for pipes, so we're
         // forced to emulate the behavior manually at the application level. To
         // do this, we need to both cancel any pending requests, as well as
@@ -536,23 +519,35 @@ impl rtio::RtioPipe for UnixStream {
         self.cancel_io()
     }
 
-    fn close_write(&mut self) -> IoResult<()> {
+    pub fn close_write(&mut self) -> IoResult<()> {
         // see comments in close_read() for why this lock is necessary
         let _guard = unsafe { self.inner.lock.lock() };
         self.inner.write_closed.store(true, atomic::SeqCst);
         self.cancel_io()
     }
 
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        let deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        let deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
         self.read_deadline = deadline;
         self.write_deadline = deadline;
     }
-    fn set_read_timeout(&mut self, timeout: Option<u64>) {
-        self.read_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_read_timeout(&mut self, timeout: Option<u64>) {
+        self.read_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
     }
-    fn set_write_timeout(&mut self, timeout: Option<u64>) {
-        self.write_deadline = timeout.map(|a| ::io::timer::now() + a).unwrap_or(0);
+    pub fn set_write_timeout(&mut self, timeout: Option<u64>) {
+        self.write_deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
+    }
+}
+
+impl Clone for UnixStream {
+    fn clone(&self) -> UnixStream {
+        UnixStream {
+            inner: self.inner.clone(),
+            read: None,
+            write: None,
+            read_deadline: 0,
+            write_deadline: 0,
+        }
     }
 }
 
@@ -570,7 +565,7 @@ impl UnixListener {
         // Although we technically don't need the pipe until much later, we
         // create the initial handle up front to test the validity of the name
         // and such.
-        let addr_v = try!(to_utf16(addr));
+        let addr_v = try!(to_utf16(addr.as_str()));
         let ret = unsafe { pipe(addr_v.as_ptr(), true) };
         if ret == libc::INVALID_HANDLE_VALUE {
             Err(super::last_error())
@@ -579,7 +574,7 @@ impl UnixListener {
         }
     }
 
-    pub fn native_listen(self) -> IoResult<UnixAcceptor> {
+    pub fn listen(self) -> IoResult<UnixAcceptor> {
         Ok(UnixAcceptor {
             listener: self,
             event: try!(Event::new(true, false)),
@@ -598,15 +593,6 @@ impl Drop for UnixListener {
     }
 }
 
-impl rtio::RtioUnixListener for UnixListener {
-    fn listen(self: Box<UnixListener>)
-              -> IoResult<Box<rtio::RtioUnixAcceptor + Send>> {
-        self.native_listen().map(|a| {
-            box a as Box<rtio::RtioUnixAcceptor + Send>
-        })
-    }
-}
-
 pub struct UnixAcceptor {
     inner: Arc<AcceptorState>,
     listener: UnixListener,
@@ -620,7 +606,7 @@ struct AcceptorState {
 }
 
 impl UnixAcceptor {
-    pub fn native_accept(&mut self) -> IoResult<UnixStream> {
+    pub fn accept(&mut self) -> IoResult<UnixStream> {
         // This function has some funky implementation details when working with
         // unix pipes. On windows, each server named pipe handle can be
         // connected to a one or zero clients. To the best of my knowledge, a
@@ -657,9 +643,9 @@ impl UnixAcceptor {
 
         // If we've had an artificial call to close_accept, be sure to never
         // proceed in accepting new clients in the future
-        if self.inner.closed.load(atomic::SeqCst) { return Err(util::eof()) }
+        if self.inner.closed.load(atomic::SeqCst) { return Err(eof()) }
 
-        let name = try!(to_utf16(&self.listener.name));
+        let name = try!(to_utf16(self.listener.name.as_str()));
 
         // Once we've got a "server handle", we need to wait for a client to
         // connect. The ConnectNamedPipe function will block this thread until
@@ -691,7 +677,7 @@ impl UnixAcceptor {
                     if wait_succeeded.is_ok() {
                         err = unsafe { libc::GetLastError() };
                     } else {
-                        return Err(util::timeout("accept timed out"))
+                        return Err(sys_common::timeout("accept timed out"))
                     }
                 } else {
                     // we succeeded, bypass the check below
@@ -727,19 +713,28 @@ impl UnixAcceptor {
             write_deadline: 0,
         })
     }
-}
 
-impl rtio::RtioUnixAcceptor for UnixAcceptor {
-    fn accept(&mut self) -> IoResult<Box<rtio::RtioPipe + Send>> {
-        self.native_accept().map(|s| box s as Box<rtio::RtioPipe + Send>)
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        self.deadline = timeout.map(|i| i + timer::now()).unwrap_or(0);
     }
-    fn set_timeout(&mut self, timeout: Option<u64>) {
-        self.deadline = timeout.map(|i| i + ::io::timer::now()).unwrap_or(0);
+
+    pub fn close_accept(&mut self) -> IoResult<()> {
+        self.inner.closed.store(true, atomic::SeqCst);
+        let ret = unsafe {
+            c::SetEvent(self.inner.abort.handle())
+        };
+        if ret == 0 {
+            Err(super::last_error())
+        } else {
+            Ok(())
+        }
     }
+}
 
-    fn clone(&self) -> Box<rtio::RtioUnixAcceptor + Send> {
-        let name = to_utf16(&self.listener.name).ok().unwrap();
-        box UnixAcceptor {
+impl Clone for UnixAcceptor {
+    fn clone(&self) -> UnixAcceptor {
+        let name = to_utf16(self.listener.name.as_str()).ok().unwrap();
+        UnixAcceptor {
             inner: self.inner.clone(),
             event: Event::new(true, false).ok().unwrap(),
             deadline: 0,
@@ -751,19 +746,6 @@ impl rtio::RtioUnixAcceptor for UnixAcceptor {
                     p
                 },
             },
-        } as Box<rtio::RtioUnixAcceptor + Send>
-    }
-
-    fn close_accept(&mut self) -> IoResult<()> {
-        self.inner.closed.store(true, atomic::SeqCst);
-        let ret = unsafe {
-            c::SetEvent(self.inner.abort.handle())
-        };
-        if ret == 0 {
-            Err(super::last_error())
-        } else {
-            Ok(())
         }
     }
 }
-
diff --git a/src/libstd/sys/windows/tcp.rs b/src/libstd/sys/windows/tcp.rs
new file mode 100644
index 0000000000000..3baf2be08d238
--- /dev/null
+++ b/src/libstd/sys/windows/tcp.rs
@@ -0,0 +1,219 @@
+// Copyright 2014 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 io::net::ip;
+use io::IoResult;
+use libc;
+use mem;
+use ptr;
+use prelude::*;
+use super::{last_error, last_net_error, retry, sock_t};
+use sync::{Arc, atomic};
+use sys::fs::FileDesc;
+use sys::{mod, c, set_nonblocking, wouldblock, timer};
+use sys_common::{mod, timeout, eof};
+use sys_common::net::*;
+
+pub use sys_common::net::TcpStream;
+
+pub struct Event(c::WSAEVENT);
+
+impl Event {
+    pub fn new() -> IoResult<Event> {
+        let event = unsafe { c::WSACreateEvent() };
+        if event == c::WSA_INVALID_EVENT {
+            Err(super::last_error())
+        } else {
+            Ok(Event(event))
+        }
+    }
+
+    pub fn handle(&self) -> c::WSAEVENT { let Event(handle) = *self; handle }
+}
+
+impl Drop for Event {
+    fn drop(&mut self) {
+        unsafe { let _ = c::WSACloseEvent(self.handle()); }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TCP listeners
+////////////////////////////////////////////////////////////////////////////////
+
+pub struct TcpListener {
+    inner: FileDesc,
+}
+
+impl TcpListener {
+    pub fn bind(addr: ip::SocketAddr) -> IoResult<TcpListener> {
+        sys::init_net();
+
+        let fd = try!(socket(addr, libc::SOCK_STREAM));
+        let ret = TcpListener { inner: FileDesc::new(fd as libc::c_int, true) };
+
+        let mut storage = unsafe { mem::zeroed() };
+        let len = addr_to_sockaddr(addr, &mut storage);
+        let addrp = &storage as *const _ as *const libc::sockaddr;
+
+        match unsafe { libc::bind(fd, addrp, len) } {
+            -1 => Err(last_net_error()),
+            _ => Ok(ret),
+        }
+    }
+
+    pub fn fd(&self) -> sock_t { self.inner.fd as sock_t }
+
+    pub fn listen(self, backlog: int) -> IoResult<TcpAcceptor> {
+        match unsafe { libc::listen(self.fd(), backlog as libc::c_int) } {
+            -1 => Err(last_net_error()),
+
+            _ => {
+                let accept = try!(Event::new());
+                let ret = unsafe {
+                    c::WSAEventSelect(self.fd(), accept.handle(), c::FD_ACCEPT)
+                };
+                if ret != 0 {
+                    return Err(last_net_error())
+                }
+                Ok(TcpAcceptor {
+                    inner: Arc::new(AcceptorInner {
+                        listener: self,
+                        abort: try!(Event::new()),
+                        accept: accept,
+                        closed: atomic::AtomicBool::new(false),
+                    }),
+                    deadline: 0,
+                })
+            }
+        }
+    }
+
+    pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
+        sockname(self.fd(), libc::getsockname)
+    }
+}
+
+pub struct TcpAcceptor {
+    inner: Arc<AcceptorInner>,
+    deadline: u64,
+}
+
+struct AcceptorInner {
+    listener: TcpListener,
+    abort: Event,
+    accept: Event,
+    closed: atomic::AtomicBool,
+}
+
+impl TcpAcceptor {
+    pub fn fd(&self) -> sock_t { self.inner.listener.fd() }
+
+    pub fn accept(&mut self) -> IoResult<TcpStream> {
+        // Unlink unix, windows cannot invoke `select` on arbitrary file
+        // descriptors like pipes, only sockets. Consequently, windows cannot
+        // use the same implementation as unix for accept() when close_accept()
+        // is considered.
+        //
+        // In order to implement close_accept() and timeouts, windows uses
+        // event handles. An acceptor-specific abort event is created which
+        // will only get set in close_accept(), and it will never be un-set.
+        // Additionally, another acceptor-specific event is associated with the
+        // FD_ACCEPT network event.
+        //
+        // These two events are then passed to WaitForMultipleEvents to see
+        // which one triggers first, and the timeout passed to this function is
+        // the local timeout for the acceptor.
+        //
+        // If the wait times out, then the accept timed out. If the wait
+        // succeeds with the abort event, then we were closed, and if the wait
+        // succeeds otherwise, then we do a nonblocking poll via `accept` to
+        // see if we can accept a connection. The connection is candidate to be
+        // stolen, so we do all of this in a loop as well.
+        let events = [self.inner.abort.handle(), self.inner.accept.handle()];
+
+        while !self.inner.closed.load(atomic::SeqCst) {
+            let ms = if self.deadline == 0 {
+                c::WSA_INFINITE as u64
+            } else {
+                let now = timer::now();
+                if self.deadline < now {0} else {self.deadline - now}
+            };
+            let ret = unsafe {
+                c::WSAWaitForMultipleEvents(2, events.as_ptr(), libc::FALSE,
+                                            ms as libc::DWORD, libc::FALSE)
+            };
+            match ret {
+                c::WSA_WAIT_TIMEOUT => {
+                    return Err(timeout("accept timed out"))
+                }
+                c::WSA_WAIT_FAILED => return Err(last_net_error()),
+                c::WSA_WAIT_EVENT_0 => break,
+                n => assert_eq!(n, c::WSA_WAIT_EVENT_0 + 1),
+            }
+
+            let mut wsaevents: c::WSANETWORKEVENTS = unsafe { mem::zeroed() };
+            let ret = unsafe {
+                c::WSAEnumNetworkEvents(self.fd(), events[1], &mut wsaevents)
+            };
+            if ret != 0 { return Err(last_net_error()) }
+
+            if wsaevents.lNetworkEvents & c::FD_ACCEPT == 0 { continue }
+            match unsafe {
+                libc::accept(self.fd(), ptr::null_mut(), ptr::null_mut())
+            } {
+                -1 if wouldblock() => {}
+                -1 => return Err(last_net_error()),
+
+                // Accepted sockets inherit the same properties as the caller,
+                // so we need to deregister our event and switch the socket back
+                // to blocking mode
+                fd => {
+                    let stream = TcpStream::new(fd);
+                    let ret = unsafe {
+                        c::WSAEventSelect(fd, events[1], 0)
+                    };
+                    if ret != 0 { return Err(last_net_error()) }
+                    try!(set_nonblocking(fd, false));
+                    return Ok(stream)
+                }
+            }
+        }
+
+        Err(eof())
+    }
+
+    pub fn socket_name(&mut self) -> IoResult<ip::SocketAddr> {
+        sockname(self.fd(), libc::getsockname)
+    }
+
+    pub fn set_timeout(&mut self, timeout: Option<u64>) {
+        self.deadline = timeout.map(|a| timer::now() + a).unwrap_or(0);
+    }
+
+    pub fn close_accept(&mut self) -> IoResult<()> {
+        self.inner.closed.store(true, atomic::SeqCst);
+        let ret = unsafe { c::WSASetEvent(self.inner.abort.handle()) };
+        if ret == libc::TRUE {
+            Ok(())
+        } else {
+            Err(last_net_error())
+        }
+    }
+}
+
+impl Clone for TcpAcceptor {
+    fn clone(&self) -> TcpAcceptor {
+        TcpAcceptor {
+            inner: self.inner.clone(),
+            deadline: 0,
+        }
+    }
+}
diff --git a/src/libstd/sys/windows/udp.rs b/src/libstd/sys/windows/udp.rs
new file mode 100644
index 0000000000000..50f8fb828ad32
--- /dev/null
+++ b/src/libstd/sys/windows/udp.rs
@@ -0,0 +1,11 @@
+// Copyright 2014 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.
+
+pub use sys_common::net::UdpSocket;

From 3d195482a45bf3ed0f12dc9d70d14192262ca711 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Wed, 15 Oct 2014 15:45:59 -0700
Subject: [PATCH 05/11] Runtime removal: refactor helper threads

This patch continues the runtime removal by moving
libnative::io::helper_thread into sys::helper_signal and
sys_common::helper_thread

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/mod.rs                       |  2 -
 .../io => libstd/sys/common}/helper_thread.rs | 83 ++++---------------
 src/libstd/sys/common/mod.rs                  |  1 +
 src/libstd/sys/common/net.rs                  |  2 +
 src/libstd/sys/unix/helper_signal.rs          | 29 +++++++
 src/libstd/sys/unix/mod.rs                    | 11 +++
 src/libstd/sys/windows/helper_signal.rs       | 38 +++++++++
 src/libstd/sys/windows/mod.rs                 |  1 +
 8 files changed, 96 insertions(+), 71 deletions(-)
 rename src/{libnative/io => libstd/sys/common}/helper_thread.rs (71%)
 create mode 100644 src/libstd/sys/unix/helper_signal.rs
 create mode 100644 src/libstd/sys/windows/helper_signal.rs

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 2a76bc29f7c37..1d0b9f40d0748 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -32,8 +32,6 @@ use std::num;
 // Local re-exports
 pub use self::process::Process;
 
-mod helper_thread;
-
 // Native I/O implementations
 pub mod process;
 mod util;
diff --git a/src/libnative/io/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs
similarity index 71%
rename from src/libnative/io/helper_thread.rs
rename to src/libstd/sys/common/helper_thread.rs
index d1368ad31f475..8c8ec4466a7c4 100644
--- a/src/libnative/io/helper_thread.rs
+++ b/src/libstd/sys/common/helper_thread.rs
@@ -22,14 +22,15 @@
 
 #![macro_escape]
 
-use std::cell::UnsafeCell;
-use std::mem;
-use std::rt::bookkeeping;
-use std::rt::mutex::StaticNativeMutex;
-use std::rt;
-use std::task::TaskBuilder;
+use mem;
+use rt::bookkeeping;
+use rt::mutex::StaticNativeMutex;
+use rt;
+use cell::UnsafeCell;
+use sys::helper_signal;
+use prelude::*;
 
-use NativeTaskBuilder;
+use task;
 
 /// A structure for management of a helper thread.
 ///
@@ -77,17 +78,17 @@ impl<M: Send> Helper<M> {
     /// This function is safe to be called many times.
     pub fn boot<T: Send>(&'static self,
                          f: || -> T,
-                         helper: fn(imp::signal, Receiver<M>, T)) {
+                         helper: fn(helper_signal::signal, Receiver<M>, T)) {
         unsafe {
             let _guard = self.lock.lock();
             if !*self.initialized.get() {
                 let (tx, rx) = channel();
                 *self.chan.get() = mem::transmute(box tx);
-                let (receive, send) = imp::new();
+                let (receive, send) = helper_signal::new();
                 *self.signal.get() = send as uint;
 
                 let t = f();
-                TaskBuilder::new().native().spawn(proc() {
+                task::spawn(proc() {
                     bookkeeping::decrement();
                     helper(receive, rx, t);
                     self.lock.lock().signal()
@@ -111,7 +112,7 @@ impl<M: Send> Helper<M> {
             // send the message.
             assert!(!self.chan.get().is_null());
             (**self.chan.get()).send(msg);
-            imp::signal(*self.signal.get() as imp::signal);
+            helper_signal::signal(*self.signal.get() as helper_signal::signal);
         }
     }
 
@@ -126,7 +127,7 @@ impl<M: Send> Helper<M> {
             let chan: Box<Sender<M>> = mem::transmute(*self.chan.get());
             *self.chan.get() = 0 as *mut Sender<M>;
             drop(chan);
-            imp::signal(*self.signal.get() as imp::signal);
+            helper_signal::signal(*self.signal.get() as helper_signal::signal);
 
             // Wait for the child to exit
             guard.wait();
@@ -134,64 +135,8 @@ impl<M: Send> Helper<M> {
 
             // Clean up after ourselves
             self.lock.destroy();
-            imp::close(*self.signal.get() as imp::signal);
+            helper_signal::close(*self.signal.get() as helper_signal::signal);
             *self.signal.get() = 0;
         }
     }
 }
-
-#[cfg(unix)]
-mod imp {
-    use libc;
-    use std::os;
-
-    use io::file::FileDesc;
-
-    pub type signal = libc::c_int;
-
-    pub fn new() -> (signal, signal) {
-        let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
-        (reader, writer)
-    }
-
-    pub fn signal(fd: libc::c_int) {
-        FileDesc::new(fd, false).inner_write([0]).ok().unwrap();
-    }
-
-    pub fn close(fd: libc::c_int) {
-        let _fd = FileDesc::new(fd, true);
-    }
-}
-
-#[cfg(windows)]
-mod imp {
-    use libc::{BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
-    use std::ptr;
-    use libc;
-
-    pub type signal = HANDLE;
-
-    pub fn new() -> (HANDLE, HANDLE) {
-        unsafe {
-            let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
-                                      ptr::null());
-            (handle, handle)
-        }
-    }
-
-    pub fn signal(handle: HANDLE) {
-        assert!(unsafe { SetEvent(handle) != 0 });
-    }
-
-    pub fn close(handle: HANDLE) {
-        assert!(unsafe { CloseHandle(handle) != 0 });
-    }
-
-    extern "system" {
-        fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
-                        bManualReset: BOOL,
-                        bInitialState: BOOL,
-                        lpName: LPCSTR) -> HANDLE;
-        fn SetEvent(hEvent: HANDLE) -> BOOL;
-    }
-}
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index 402c62bb35e4f..75c2987078dc3 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -20,6 +20,7 @@ use path::BytesContainer;
 use collections;
 
 pub mod net;
+pub mod helper_thread;
 
 // common error constructors
 
diff --git a/src/libstd/sys/common/net.rs b/src/libstd/sys/common/net.rs
index 0559005100f90..7c44142d93cdb 100644
--- a/src/libstd/sys/common/net.rs
+++ b/src/libstd/sys/common/net.rs
@@ -24,6 +24,8 @@ use prelude::*;
 use cmp;
 use io;
 
+// FIXME: move uses of Arc and deadline tracking to std::io
+
 #[deriving(Show)]
 pub enum SocketStatus {
     Readable,
diff --git a/src/libstd/sys/unix/helper_signal.rs b/src/libstd/sys/unix/helper_signal.rs
new file mode 100644
index 0000000000000..a806bea2568d2
--- /dev/null
+++ b/src/libstd/sys/unix/helper_signal.rs
@@ -0,0 +1,29 @@
+// Copyright 2014 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 libc;
+use os;
+
+use sys::fs::FileDesc;
+
+pub type signal = libc::c_int;
+
+pub fn new() -> (signal, signal) {
+    let os::Pipe { reader, writer } = unsafe { os::pipe().unwrap() };
+    (reader, writer)
+}
+
+pub fn signal(fd: libc::c_int) {
+    FileDesc::new(fd, false).write([0]).ok().unwrap();
+}
+
+pub fn close(fd: libc::c_int) {
+    let _fd = FileDesc::new(fd, true);
+}
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 5a43fd08f9047..6295864e0e107 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -17,12 +17,23 @@ use prelude::*;
 use io::{mod, IoResult, IoError};
 use sys_common::mkerr_libc;
 
+
+macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
+    static $name: Helper<$m> = Helper {
+        lock: ::rt::mutex::NATIVE_MUTEX_INIT,
+        chan: ::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
+        signal: ::cell::UnsafeCell { value: 0 },
+        initialized: ::cell::UnsafeCell { value: false },
+    };
+) )
+
 pub mod c;
 pub mod fs;
 pub mod os;
 pub mod tcp;
 pub mod udp;
 pub mod pipe;
+pub mod helper_signal;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libstd/sys/windows/helper_signal.rs b/src/libstd/sys/windows/helper_signal.rs
new file mode 100644
index 0000000000000..c547c79e83a13
--- /dev/null
+++ b/src/libstd/sys/windows/helper_signal.rs
@@ -0,0 +1,38 @@
+// Copyright 2014 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 libc::{mod, BOOL, LPCSTR, HANDLE, LPSECURITY_ATTRIBUTES, CloseHandle};
+use ptr;
+
+pub type signal = HANDLE;
+
+pub fn new() -> (HANDLE, HANDLE) {
+    unsafe {
+        let handle = CreateEventA(ptr::null_mut(), libc::FALSE, libc::FALSE,
+                                  ptr::null());
+        (handle, handle)
+    }
+}
+
+pub fn signal(handle: HANDLE) {
+    assert!(unsafe { SetEvent(handle) != 0 });
+}
+
+pub fn close(handle: HANDLE) {
+    assert!(unsafe { CloseHandle(handle) != 0 });
+}
+
+extern "system" {
+    fn CreateEventA(lpSecurityAttributes: LPSECURITY_ATTRIBUTES,
+                    bManualReset: BOOL,
+                    bInitialState: BOOL,
+                    lpName: LPCSTR) -> HANDLE;
+    fn SetEvent(hEvent: HANDLE) -> BOOL;
+}
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 85fbc6b936c30..6f6ca3f2e6258 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -39,6 +39,7 @@ pub mod os;
 pub mod tcp;
 pub mod udp;
 pub mod pipe;
+pub mod helper_signal;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;

From 0f98e75b69d16edce9ca60d7961b8440856a3f72 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Thu, 9 Oct 2014 16:27:28 -0700
Subject: [PATCH 06/11] Runtime removal: refactor process

This patch continues the runtime removal by moving and refactoring the
process implementation into the new `sys` module.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/mod.rs                       |  20 -
 src/librustrt/rtio.rs                         |  66 --
 src/libstd/io/process.rs                      | 228 ++++---
 src/libstd/sys/common/helper_thread.rs        |  11 -
 src/libstd/{platform_imp => sys}/unix/fs.rs   |   0
 src/libstd/sys/unix/mod.rs                    |   2 +-
 src/libstd/sys/unix/process.rs                | 587 ++++++++++++++++++
 .../{platform_imp => sys}/windows/fs.rs       |   0
 src/libstd/sys/windows/mod.rs                 |   1 +
 src/libstd/sys/windows/process.rs             | 511 +++++++++++++++
 10 files changed, 1250 insertions(+), 176 deletions(-)
 rename src/libstd/{platform_imp => sys}/unix/fs.rs (100%)
 create mode 100644 src/libstd/sys/unix/process.rs
 rename src/libstd/{platform_imp => sys}/windows/fs.rs (100%)
 create mode 100644 src/libstd/sys/windows/process.rs

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 1d0b9f40d0748..29370dee88b4d 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -29,13 +29,6 @@ use std::os;
 use std::rt::rtio::{mod, IoResult, IoError};
 use std::num;
 
-// Local re-exports
-pub use self::process::Process;
-
-// Native I/O implementations
-pub mod process;
-mod util;
-
 #[cfg(any(target_os = "macos",
           target_os = "ios",
           target_os = "freebsd",
@@ -123,19 +116,6 @@ impl rtio::IoFactory for IoFactory {
     fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
         timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
     }
-    fn spawn(&mut self, cfg: rtio::ProcessConfig)
-            -> IoResult<(Box<rtio::RtioProcess + Send>,
-                         Vec<Option<Box<rtio::RtioPipe + Send>>>)> {
-        process::Process::spawn(cfg).map(|(p, io)| {
-            (box p as Box<rtio::RtioProcess + Send>,
-             io.into_iter().map(|p| p.map(|p| {
-                 box p as Box<rtio::RtioPipe + Send>
-             })).collect())
-        })
-    }
-    fn kill(&mut self, pid: libc::pid_t, signum: int) -> IoResult<()> {
-        process::Process::kill(pid, signum)
-    }
     #[cfg(unix)]
     fn tty_open(&mut self, fd: c_int, _readable: bool)
                 -> IoResult<Box<rtio::RtioTTY + Send>> {
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index 3ebfcaea687f1..cdcefc2088e7c 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -46,61 +46,6 @@ pub trait RemoteCallback {
     fn fire(&mut self);
 }
 
-/// Data needed to spawn a process. Serializes the `std::io::process::Command`
-/// builder.
-pub struct ProcessConfig<'a> {
-    /// Path to the program to run.
-    pub program: &'a CString,
-
-    /// Arguments to pass to the program (doesn't include the program itself).
-    pub args: &'a [CString],
-
-    /// Optional environment to specify for the program. If this is None, then
-    /// it will inherit the current process's environment.
-    pub env: Option<&'a [(&'a CString, &'a CString)]>,
-
-    /// Optional working directory for the new process. If this is None, then
-    /// the current directory of the running process is inherited.
-    pub cwd: Option<&'a CString>,
-
-    /// Configuration for the child process's stdin handle (file descriptor 0).
-    /// This field defaults to `CreatePipe(true, false)` so the input can be
-    /// written to.
-    pub stdin: StdioContainer,
-
-    /// Configuration for the child process's stdout handle (file descriptor 1).
-    /// This field defaults to `CreatePipe(false, true)` so the output can be
-    /// collected.
-    pub stdout: StdioContainer,
-
-    /// Configuration for the child process's stdout handle (file descriptor 2).
-    /// This field defaults to `CreatePipe(false, true)` so the output can be
-    /// collected.
-    pub stderr: StdioContainer,
-
-    /// Any number of streams/file descriptors/pipes may be attached to this
-    /// process. This list enumerates the file descriptors and such for the
-    /// process to be spawned, and the file descriptors inherited will start at
-    /// 3 and go to the length of this array. The first three file descriptors
-    /// (stdin/stdout/stderr) are configured with the `stdin`, `stdout`, and
-    /// `stderr` fields.
-    pub extra_io: &'a [StdioContainer],
-
-    /// Sets the child process's user id. This translates to a `setuid` call in
-    /// the child process. Setting this value on windows will cause the spawn to
-    /// fail. Failure in the `setuid` call on unix will also cause the spawn to
-    /// fail.
-    pub uid: Option<uint>,
-
-    /// Similar to `uid`, but sets the group id of the child process. This has
-    /// the same semantics as the `uid` field.
-    pub gid: Option<uint>,
-
-    /// If true, the child process is spawned in a detached state. On unix, this
-    /// means that the child is the leader of a new process group.
-    pub detach: bool,
-}
-
 pub struct LocalIo<'a> {
     factory: &'a mut IoFactory+'a,
 }
@@ -170,10 +115,6 @@ impl<'a> LocalIo<'a> {
 
 pub trait IoFactory {
     fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
-    fn spawn(&mut self, cfg: ProcessConfig)
-            -> IoResult<(Box<RtioProcess + Send>,
-                         Vec<Option<Box<RtioPipe + Send>>>)>;
-    fn kill(&mut self, pid: libc::pid_t, signal: int) -> IoResult<()>;
     fn tty_open(&mut self, fd: c_int, readable: bool)
             -> IoResult<Box<RtioTTY + Send>>;
 }
@@ -184,13 +125,6 @@ pub trait RtioTimer {
     fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
 }
 
-pub trait RtioProcess {
-    fn id(&self) -> libc::pid_t;
-    fn kill(&mut self, signal: int) -> IoResult<()>;
-    fn wait(&mut self) -> IoResult<ProcessExit>;
-    fn set_timeout(&mut self, timeout: Option<u64>);
-}
-
 pub trait RtioPipe {
     fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
     fn write(&mut self, buf: &[u8]) -> IoResult<()>;
diff --git a/src/libstd/io/process.rs b/src/libstd/io/process.rs
index 698e0a3460f28..d71bab0b48f90 100644
--- a/src/libstd/io/process.rs
+++ b/src/libstd/io/process.rs
@@ -20,14 +20,17 @@ use os;
 use io::{IoResult, IoError};
 use io;
 use libc;
-use mem;
-use rt::rtio::{RtioProcess, ProcessConfig, IoFactory, LocalIo};
-use rt::rtio;
 use c_str::CString;
 use collections::HashMap;
 use hash::Hash;
 #[cfg(windows)]
 use std::hash::sip::SipState;
+use io::pipe::{PipeStream, PipePair};
+use path::BytesContainer;
+
+use sys;
+use sys::fs::FileDesc;
+use sys::process::Process as ProcessImp;
 
 /// Signal a process to exit, without forcibly killing it. Corresponds to
 /// SIGTERM on unix platforms.
@@ -62,24 +65,29 @@ use std::hash::sip::SipState;
 /// assert!(child.wait().unwrap().success());
 /// ```
 pub struct Process {
-    handle: Box<RtioProcess + Send>,
+    handle: ProcessImp,
     forget: bool,
 
+    /// None until wait() is called.
+    exit_code: Option<ProcessExit>,
+
+    /// Manually delivered signal
+    exit_signal: Option<int>,
+
+    /// Deadline after which wait() will return
+    deadline: u64,
+
     /// Handle to the child's stdin, if the `stdin` field of this process's
     /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
-    pub stdin: Option<io::PipeStream>,
+    pub stdin: Option<PipeStream>,
 
     /// Handle to the child's stdout, if the `stdout` field of this process's
     /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
-    pub stdout: Option<io::PipeStream>,
+    pub stdout: Option<PipeStream>,
 
     /// Handle to the child's stderr, if the `stderr` field of this process's
     /// `ProcessConfig` was `CreatePipe`. By default, this handle is `Some`.
-    pub stderr: Option<io::PipeStream>,
-
-    /// Extra I/O handles as configured by the original `ProcessConfig` when
-    /// this process was created. This is by default empty.
-    pub extra_io: Vec<Option<io::PipeStream>>,
+    pub stderr: Option<PipeStream>,
 }
 
 /// A representation of environment variable name
@@ -130,6 +138,13 @@ impl PartialEq for EnvKey {
     }
 }
 
+impl BytesContainer for EnvKey {
+    fn container_as_bytes<'a>(&'a self) -> &'a [u8] {
+        let &EnvKey(ref k) = self;
+        k.container_as_bytes()
+    }
+}
+
 /// A HashMap representation of environment variables.
 pub type EnvMap = HashMap<EnvKey, CString>;
 
@@ -160,7 +175,6 @@ pub struct Command {
     stdin: StdioContainer,
     stdout: StdioContainer,
     stderr: StdioContainer,
-    extra_io: Vec<StdioContainer>,
     uid: Option<uint>,
     gid: Option<uint>,
     detach: bool,
@@ -194,7 +208,6 @@ impl Command {
             stdin: CreatePipe(true, false),
             stdout: CreatePipe(false, true),
             stderr: CreatePipe(false, true),
-            extra_io: Vec::new(),
             uid: None,
             gid: None,
             detach: false,
@@ -281,14 +294,6 @@ impl Command {
         self.stderr = cfg;
         self
     }
-    /// Attaches a stream/file descriptor/pipe to the child process. Inherited
-    /// file descriptors are numbered consecutively, starting at 3; the first
-    /// three file descriptors (stdin/stdout/stderr) are configured with the
-    /// `stdin`, `stdout`, and `stderr` methods.
-    pub fn extra_io<'a>(&'a mut self, cfg: StdioContainer) -> &'a mut Command {
-        self.extra_io.push(cfg);
-        self
-    }
 
     /// Sets the child process's user id. This translates to a `setuid` call in
     /// the child process. Setting this value on windows will cause the spawn to
@@ -315,50 +320,23 @@ impl Command {
 
     /// Executes the command as a child process, which is returned.
     pub fn spawn(&self) -> IoResult<Process> {
-        fn to_rtio(p: StdioContainer) -> rtio::StdioContainer {
-            match p {
-                Ignored => rtio::Ignored,
-                InheritFd(fd) => rtio::InheritFd(fd),
-                CreatePipe(a, b) => rtio::CreatePipe(a, b),
-            }
-        }
-        let extra_io: Vec<rtio::StdioContainer> =
-            self.extra_io.iter().map(|x| to_rtio(*x)).collect();
-        LocalIo::maybe_raise(|io| {
-            let env = match self.env {
-                None => None,
-                Some(ref env_map) =>
-                    Some(env_map.iter()
-                                .map(|(&EnvKey(ref key), val)| (key, val))
-                                .collect::<Vec<_>>())
-            };
-            let cfg = ProcessConfig {
-                program: &self.program,
-                args: self.args.as_slice(),
-                env: env.as_ref().map(|e| e.as_slice()),
-                cwd: self.cwd.as_ref(),
-                stdin: to_rtio(self.stdin),
-                stdout: to_rtio(self.stdout),
-                stderr: to_rtio(self.stderr),
-                extra_io: extra_io.as_slice(),
-                uid: self.uid,
-                gid: self.gid,
-                detach: self.detach,
-            };
-            io.spawn(cfg).map(|(p, io)| {
-                let mut io = io.into_iter().map(|p| {
-                    p.map(|p| io::PipeStream::new(p))
-                });
-                Process {
-                    handle: p,
-                    forget: false,
-                    stdin: io.next().unwrap(),
-                    stdout: io.next().unwrap(),
-                    stderr: io.next().unwrap(),
-                    extra_io: io.collect(),
-                }
+        let (their_stdin, our_stdin) = try!(setup_io(self.stdin));
+        let (their_stdout, our_stdout) = try!(setup_io(self.stdout));
+        let (their_stderr, our_stderr) = try!(setup_io(self.stderr));
+
+        match ProcessImp::spawn(self, their_stdin, their_stdout, their_stderr) {
+            Err(e) => Err(e),
+            Ok(handle) => Ok(Process {
+                handle: handle,
+                forget: false,
+                exit_code: None,
+                exit_signal: None,
+                deadline: 0,
+                stdin: our_stdin,
+                stdout: our_stdout,
+                stderr: our_stderr,
             })
-        }).map_err(IoError::from_rtio_error)
+        }
     }
 
     /// Executes the command as a child process, waiting for it to finish and
@@ -415,6 +393,58 @@ impl fmt::Show for Command {
     }
 }
 
+fn setup_io(io: StdioContainer) -> IoResult<(Option<PipeStream>, Option<PipeStream>)> {
+    let ours;
+    let theirs;
+    match io {
+        Ignored => {
+            theirs = None;
+            ours = None;
+        }
+        InheritFd(fd) => {
+            theirs = Some(PipeStream::from_filedesc(FileDesc::new(fd, false)));
+            ours = None;
+        }
+        CreatePipe(readable, _writable) => {
+            let PipePair { reader, writer } = try!(PipeStream::pair());
+            if readable {
+                theirs = Some(reader);
+                ours = Some(writer);
+            } else {
+                theirs = Some(writer);
+                ours = Some(reader);
+            }
+        }
+    }
+    Ok((theirs, ours))
+}
+
+// Allow the sys module to get access to the Command state
+impl sys::process::ProcessConfig<EnvKey, CString> for Command {
+    fn program(&self) -> &CString {
+        &self.program
+    }
+    fn args(&self) -> &[CString] {
+        self.args.as_slice()
+    }
+    fn env(&self) -> Option<&EnvMap> {
+        self.env.as_ref()
+    }
+    fn cwd(&self) -> Option<&CString> {
+        self.cwd.as_ref()
+    }
+    fn uid(&self) -> Option<uint> {
+        self.uid.clone()
+    }
+    fn gid(&self) -> Option<uint> {
+        self.gid.clone()
+    }
+    fn detach(&self) -> bool {
+        self.detach
+    }
+
+}
+
 /// The output of a finished process.
 #[deriving(PartialEq, Eq, Clone)]
 pub struct ProcessOutput {
@@ -494,9 +524,7 @@ impl Process {
     /// be successfully delivered if the child has exited, but not yet been
     /// reaped.
     pub fn kill(id: libc::pid_t, signal: int) -> IoResult<()> {
-        LocalIo::maybe_raise(|io| {
-            io.kill(id, signal)
-        }).map_err(IoError::from_rtio_error)
+        unsafe { ProcessImp::killpid(id, signal) }
     }
 
     /// Returns the process id of this child process
@@ -518,7 +546,42 @@ impl Process {
     ///
     /// If the signal delivery fails, the corresponding error is returned.
     pub fn signal(&mut self, signal: int) -> IoResult<()> {
-        self.handle.kill(signal).map_err(IoError::from_rtio_error)
+        #[cfg(unix)] fn collect_status(p: &mut Process) {
+            // On Linux (and possibly other unices), a process that has exited will
+            // continue to accept signals because it is "defunct". The delivery of
+            // signals will only fail once the child has been reaped. For this
+            // reason, if the process hasn't exited yet, then we attempt to collect
+            // their status with WNOHANG.
+            if p.exit_code.is_none() {
+                match p.handle.try_wait() {
+                    Some(code) => { p.exit_code = Some(code); }
+                    None => {}
+                }
+            }
+        }
+        #[cfg(windows)] fn collect_status(_p: &mut Process) {}
+
+        collect_status(self);
+
+        // if the process has finished, and therefore had waitpid called,
+        // and we kill it, then on unix we might ending up killing a
+        // newer process that happens to have the same (re-used) id
+        if self.exit_code.is_some() {
+            return Err(IoError {
+                kind: io::InvalidInput,
+                desc: "invalid argument: can't kill an exited process",
+                detail: None,
+            })
+        }
+
+        // A successfully delivered signal that isn't 0 (just a poll for being
+        // alive) is recorded for windows (see wait())
+        match unsafe { self.handle.kill(signal) } {
+            Ok(()) if signal == 0 => Ok(()),
+            Ok(()) => { self.exit_signal = Some(signal); Ok(()) }
+            Err(e) => Err(e),
+        }
+
     }
 
     /// Sends a signal to this child requesting that it exits. This is
@@ -545,10 +608,21 @@ impl Process {
     /// `set_timeout` and the timeout expires before the child exits.
     pub fn wait(&mut self) -> IoResult<ProcessExit> {
         drop(self.stdin.take());
-        match self.handle.wait() {
-            Ok(rtio::ExitSignal(s)) => Ok(ExitSignal(s)),
-            Ok(rtio::ExitStatus(s)) => Ok(ExitStatus(s)),
-            Err(e) => Err(IoError::from_rtio_error(e)),
+        match self.exit_code {
+            Some(code) => Ok(code),
+            None => {
+                let code = try!(self.handle.wait(self.deadline));
+                // On windows, waitpid will never return a signal. If a signal
+                // was successfully delivered to the process, however, we can
+                // consider it as having died via a signal.
+                let code = match self.exit_signal {
+                    None => code,
+                    Some(signal) if cfg!(windows) => ExitSignal(signal),
+                    Some(..) => code,
+                };
+                self.exit_code = Some(code);
+                Ok(code)
+            }
         }
     }
 
@@ -594,7 +668,7 @@ impl Process {
     /// ```
     #[experimental = "the type of the timeout is likely to change"]
     pub fn set_timeout(&mut self, timeout_ms: Option<u64>) {
-        self.handle.set_timeout(timeout_ms)
+        self.deadline = timeout_ms.map(|i| i + sys::timer::now()).unwrap_or(0);
     }
 
     /// Simultaneously wait for the child to exit and collect all remaining
@@ -653,7 +727,6 @@ impl Drop for Process {
         drop(self.stdin.take());
         drop(self.stdout.take());
         drop(self.stderr.take());
-        drop(mem::replace(&mut self.extra_io, Vec::new()));
 
         self.set_timeout(None);
         let _ = self.wait().unwrap();
@@ -1109,8 +1182,7 @@ mod tests {
 
     #[test]
     fn dont_close_fd_on_command_spawn() {
-        use std::rt::rtio::{Truncate, Write};
-        use self::native::io::file;
+        use sys::fs;
 
         let path = if cfg!(windows) {
             Path::new("NUL")
@@ -1118,7 +1190,7 @@ mod tests {
             Path::new("/dev/null")
         };
 
-        let mut fdes = match file::open(&path.to_c_str(), Truncate, Write) {
+        let mut fdes = match fs::open(&path, Truncate, Write) {
             Ok(f) => f,
             Err(_) => panic!("failed to open file descriptor"),
         };
@@ -1126,7 +1198,7 @@ mod tests {
         let mut cmd = pwd_cmd();
         let _ = cmd.stdout(InheritFd(fdes.fd()));
         assert!(cmd.status().unwrap().success());
-        assert!(fdes.inner_write("extra write\n".as_bytes()).is_ok());
+        assert!(fdes.write("extra write\n".as_bytes()).is_ok());
     }
 
     #[test]
diff --git a/src/libstd/sys/common/helper_thread.rs b/src/libstd/sys/common/helper_thread.rs
index 8c8ec4466a7c4..87907fde2772a 100644
--- a/src/libstd/sys/common/helper_thread.rs
+++ b/src/libstd/sys/common/helper_thread.rs
@@ -20,8 +20,6 @@
 //! can be created in the future and there must be no active timers at that
 //! time.
 
-#![macro_escape]
-
 use mem;
 use rt::bookkeeping;
 use rt::mutex::StaticNativeMutex;
@@ -57,15 +55,6 @@ pub struct Helper<M> {
     pub initialized: UnsafeCell<bool>,
 }
 
-macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
-    static $name: Helper<$m> = Helper {
-        lock: ::std::rt::mutex::NATIVE_MUTEX_INIT,
-        chan: ::std::cell::UnsafeCell { value: 0 as *mut Sender<$m> },
-        signal: ::std::cell::UnsafeCell { value: 0 },
-        initialized: ::std::cell::UnsafeCell { value: false },
-    };
-) )
-
 impl<M: Send> Helper<M> {
     /// Lazily boots a helper thread, becoming a no-op if the helper has already
     /// been spawned.
diff --git a/src/libstd/platform_imp/unix/fs.rs b/src/libstd/sys/unix/fs.rs
similarity index 100%
rename from src/libstd/platform_imp/unix/fs.rs
rename to src/libstd/sys/unix/fs.rs
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 6295864e0e107..b404dc7fdbd29 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -17,7 +17,6 @@ use prelude::*;
 use io::{mod, IoResult, IoError};
 use sys_common::mkerr_libc;
 
-
 macro_rules! helper_init( (static $name:ident: Helper<$m:ty>) => (
     static $name: Helper<$m> = Helper {
         lock: ::rt::mutex::NATIVE_MUTEX_INIT,
@@ -34,6 +33,7 @@ pub mod tcp;
 pub mod udp;
 pub mod pipe;
 pub mod helper_signal;
+pub mod process;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libstd/sys/unix/process.rs b/src/libstd/sys/unix/process.rs
new file mode 100644
index 0000000000000..0965d98d9b033
--- /dev/null
+++ b/src/libstd/sys/unix/process.rs
@@ -0,0 +1,587 @@
+// Copyright 2014 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 libc::{mod, pid_t, c_void, c_int};
+use c_str::CString;
+use io::{mod, IoResult, IoError};
+use mem;
+use os;
+use ptr;
+use prelude::*;
+use io::process::{ProcessExit, ExitStatus, ExitSignal};
+use collections;
+use path::BytesContainer;
+use hash::Hash;
+
+use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval};
+use sys::fs::FileDesc;
+use sys_common::helper_thread::Helper;
+use sys_common::{AsFileDesc, mkerr_libc, timeout};
+
+pub use sys_common::ProcessConfig;
+
+helper_init!(static HELPER: Helper<Req>)
+
+/// The unique id of the process (this should never be negative).
+pub struct Process {
+    pub pid: pid_t
+}
+
+enum Req {
+    NewChild(libc::pid_t, Sender<ProcessExit>, u64),
+}
+
+impl Process {
+    pub fn id(&self) -> pid_t {
+        self.pid
+    }
+
+    pub unsafe fn kill(&self, signal: int) -> IoResult<()> {
+        Process::killpid(self.pid, signal)
+    }
+
+    pub unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
+        let r = libc::funcs::posix88::signal::kill(pid, signal as c_int);
+        mkerr_libc(r)
+    }
+
+    pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
+                              out_fd: Option<P>, err_fd: Option<P>)
+                              -> IoResult<Process>
+        where C: ProcessConfig<K, V>, P: AsFileDesc,
+              K: BytesContainer + Eq + Hash, V: BytesContainer
+    {
+        use libc::funcs::posix88::unistd::{fork, dup2, close, chdir, execvp};
+        use libc::funcs::bsd44::getdtablesize;
+
+        mod rustrt {
+            extern {
+                pub fn rust_unset_sigprocmask();
+            }
+        }
+
+        #[cfg(target_os = "macos")]
+        unsafe fn set_environ(envp: *const c_void) {
+            extern { fn _NSGetEnviron() -> *mut *const c_void; }
+
+            *_NSGetEnviron() = envp;
+        }
+        #[cfg(not(target_os = "macos"))]
+        unsafe fn set_environ(envp: *const c_void) {
+            extern { static mut environ: *const c_void; }
+            environ = envp;
+        }
+
+        unsafe fn set_cloexec(fd: c_int) {
+            let ret = c::ioctl(fd, c::FIOCLEX);
+            assert_eq!(ret, 0);
+        }
+
+        let dirp = cfg.cwd().map(|c| c.as_ptr()).unwrap_or(ptr::null());
+
+        // temporary until unboxed closures land
+        let cfg = unsafe {
+            mem::transmute::<&ProcessConfig<K,V>,&'static ProcessConfig<K,V>>(cfg)
+        };
+
+        with_envp(cfg.env(), proc(envp) {
+            with_argv(cfg.program(), cfg.args(), proc(argv) unsafe {
+                let (input, mut output) = try!(sys::os::pipe());
+
+                // We may use this in the child, so perform allocations before the
+                // fork
+                let devnull = "/dev/null".to_c_str();
+
+                set_cloexec(output.fd());
+
+                let pid = fork();
+                if pid < 0 {
+                    return Err(super::last_error())
+                } else if pid > 0 {
+                    drop(output);
+                    let mut bytes = [0, ..4];
+                    return match input.read(bytes) {
+                        Ok(4) => {
+                            let errno = (bytes[0] as i32 << 24) |
+                                        (bytes[1] as i32 << 16) |
+                                        (bytes[2] as i32 <<  8) |
+                                        (bytes[3] as i32 <<  0);
+                            Err(super::decode_error(errno))
+                        }
+                        Err(..) => Ok(Process { pid: pid }),
+                        Ok(..) => panic!("short read on the cloexec pipe"),
+                    };
+                }
+
+                // And at this point we've reached a special time in the life of the
+                // child. The child must now be considered hamstrung and unable to
+                // do anything other than syscalls really. Consider the following
+                // scenario:
+                //
+                //      1. Thread A of process 1 grabs the malloc() mutex
+                //      2. Thread B of process 1 forks(), creating thread C
+                //      3. Thread C of process 2 then attempts to malloc()
+                //      4. The memory of process 2 is the same as the memory of
+                //         process 1, so the mutex is locked.
+                //
+                // This situation looks a lot like deadlock, right? It turns out
+                // that this is what pthread_atfork() takes care of, which is
+                // presumably implemented across platforms. The first thing that
+                // threads to *before* forking is to do things like grab the malloc
+                // mutex, and then after the fork they unlock it.
+                //
+                // Despite this information, libnative's spawn has been witnessed to
+                // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but
+                // all collected backtraces point at malloc/free traffic in the
+                // child spawned process.
+                //
+                // For this reason, the block of code below should contain 0
+                // invocations of either malloc of free (or their related friends).
+                //
+                // As an example of not having malloc/free traffic, we don't close
+                // this file descriptor by dropping the FileDesc (which contains an
+                // allocation). Instead we just close it manually. This will never
+                // have the drop glue anyway because this code never returns (the
+                // child will either exec() or invoke libc::exit)
+                let _ = libc::close(input.fd());
+
+                fn fail(output: &mut FileDesc) -> ! {
+                    let errno = sys::os::errno();
+                    let bytes = [
+                        (errno >> 24) as u8,
+                        (errno >> 16) as u8,
+                        (errno >>  8) as u8,
+                        (errno >>  0) as u8,
+                    ];
+                    assert!(output.write(bytes).is_ok());
+                    unsafe { libc::_exit(1) }
+                }
+
+                rustrt::rust_unset_sigprocmask();
+
+                // If a stdio file descriptor is set to be ignored (via a -1 file
+                // descriptor), then we don't actually close it, but rather open
+                // up /dev/null into that file descriptor. Otherwise, the first file
+                // descriptor opened up in the child would be numbered as one of the
+                // stdio file descriptors, which is likely to wreak havoc.
+                let setup = |src: Option<P>, dst: c_int| {
+                    let src = match src {
+                        None => {
+                            let flags = if dst == libc::STDIN_FILENO {
+                                libc::O_RDONLY
+                            } else {
+                                libc::O_RDWR
+                            };
+                            libc::open(devnull.as_ptr(), flags, 0)
+                        }
+                        Some(obj) => {
+                            let fd = obj.as_fd().fd();
+                            // Leak the memory and the file descriptor. We're in the
+                            // child now an all our resources are going to be
+                            // cleaned up very soon
+                            mem::forget(obj);
+                            fd
+                        }
+                    };
+                    src != -1 && retry(|| dup2(src, dst)) != -1
+                };
+
+                if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) }
+                if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) }
+                if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) }
+
+                // close all other fds
+                for fd in range(3, getdtablesize()).rev() {
+                    if fd != output.fd() {
+                        let _ = close(fd as c_int);
+                    }
+                }
+
+                match cfg.gid() {
+                    Some(u) => {
+                        if libc::setgid(u as libc::gid_t) != 0 {
+                            fail(&mut output);
+                        }
+                    }
+                    None => {}
+                }
+                match cfg.uid() {
+                    Some(u) => {
+                        // When dropping privileges from root, the `setgroups` call
+                        // will remove any extraneous groups. If we don't call this,
+                        // then even though our uid has dropped, we may still have
+                        // groups that enable us to do super-user things. This will
+                        // fail if we aren't root, so don't bother checking the
+                        // return value, this is just done as an optimistic
+                        // privilege dropping function.
+                        extern {
+                            fn setgroups(ngroups: libc::c_int,
+                                         ptr: *const libc::c_void) -> libc::c_int;
+                        }
+                        let _ = setgroups(0, 0 as *const libc::c_void);
+
+                        if libc::setuid(u as libc::uid_t) != 0 {
+                            fail(&mut output);
+                        }
+                    }
+                    None => {}
+                }
+                if cfg.detach() {
+                    // Don't check the error of setsid because it fails if we're the
+                    // process leader already. We just forked so it shouldn't return
+                    // error, but ignore it anyway.
+                    let _ = libc::setsid();
+                }
+                if !dirp.is_null() && chdir(dirp) == -1 {
+                    fail(&mut output);
+                }
+                if !envp.is_null() {
+                    set_environ(envp);
+                }
+                let _ = execvp(*argv, argv as *mut _);
+                fail(&mut output);
+            })
+        })
+    }
+
+    pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
+        use std::cmp;
+        use std::comm;
+
+        static mut WRITE_FD: libc::c_int = 0;
+
+        let mut status = 0 as c_int;
+        if deadline == 0 {
+            return match retry(|| unsafe { c::waitpid(self.pid, &mut status, 0) }) {
+                -1 => panic!("unknown waitpid error: {}", super::last_error()),
+                _ => Ok(translate_status(status)),
+            }
+        }
+
+        // On unix, wait() and its friends have no timeout parameters, so there is
+        // no way to time out a thread in wait(). From some googling and some
+        // thinking, it appears that there are a few ways to handle timeouts in
+        // wait(), but the only real reasonable one for a multi-threaded program is
+        // to listen for SIGCHLD.
+        //
+        // With this in mind, the waiting mechanism with a timeout barely uses
+        // waitpid() at all. There are a few times that waitpid() is invoked with
+        // WNOHANG, but otherwise all the necessary blocking is done by waiting for
+        // a SIGCHLD to arrive (and that blocking has a timeout). Note, however,
+        // that waitpid() is still used to actually reap the child.
+        //
+        // Signal handling is super tricky in general, and this is no exception. Due
+        // to the async nature of SIGCHLD, we use the self-pipe trick to transmit
+        // data out of the signal handler to the rest of the application. The first
+        // idea would be to have each thread waiting with a timeout to read this
+        // output file descriptor, but a write() is akin to a signal(), not a
+        // broadcast(), so it would only wake up one thread, and possibly the wrong
+        // thread. Hence a helper thread is used.
+        //
+        // The helper thread here is responsible for farming requests for a
+        // waitpid() with a timeout, and then processing all of the wait requests.
+        // By guaranteeing that only this helper thread is reading half of the
+        // self-pipe, we're sure that we'll never lose a SIGCHLD. This helper thread
+        // is also responsible for select() to wait for incoming messages or
+        // incoming SIGCHLD messages, along with passing an appropriate timeout to
+        // select() to wake things up as necessary.
+        //
+        // The ordering of the following statements is also very purposeful. First,
+        // we must be guaranteed that the helper thread is booted and available to
+        // receive SIGCHLD signals, and then we must also ensure that we do a
+        // nonblocking waitpid() at least once before we go ask the sigchld helper.
+        // This prevents the race where the child exits, we boot the helper, and
+        // then we ask for the child's exit status (never seeing a sigchld).
+        //
+        // The actual communication between the helper thread and this thread is
+        // quite simple, just a channel moving data around.
+
+        unsafe { HELPER.boot(register_sigchld, waitpid_helper) }
+
+        match self.try_wait() {
+            Some(ret) => return Ok(ret),
+            None => {}
+        }
+
+        let (tx, rx) = channel();
+        unsafe { HELPER.send(NewChild(self.pid, tx, deadline)); }
+        return match rx.recv_opt() {
+            Ok(e) => Ok(e),
+            Err(()) => Err(timeout("wait timed out")),
+        };
+
+        // Register a new SIGCHLD handler, returning the reading half of the
+        // self-pipe plus the old handler registered (return value of sigaction).
+        //
+        // Be sure to set up the self-pipe first because as soon as we register a
+        // handler we're going to start receiving signals.
+        fn register_sigchld() -> (libc::c_int, c::sigaction) {
+            unsafe {
+                let mut pipes = [0, ..2];
+                assert_eq!(libc::pipe(pipes.as_mut_ptr()), 0);
+                set_nonblocking(pipes[0], true).ok().unwrap();
+                set_nonblocking(pipes[1], true).ok().unwrap();
+                WRITE_FD = pipes[1];
+
+                let mut old: c::sigaction = mem::zeroed();
+                let mut new: c::sigaction = mem::zeroed();
+                new.sa_handler = sigchld_handler;
+                new.sa_flags = c::SA_NOCLDSTOP;
+                assert_eq!(c::sigaction(c::SIGCHLD, &new, &mut old), 0);
+                (pipes[0], old)
+            }
+        }
+
+        // Helper thread for processing SIGCHLD messages
+        fn waitpid_helper(input: libc::c_int,
+                          messages: Receiver<Req>,
+                          (read_fd, old): (libc::c_int, c::sigaction)) {
+            set_nonblocking(input, true).ok().unwrap();
+            let mut set: c::fd_set = unsafe { mem::zeroed() };
+            let mut tv: libc::timeval;
+            let mut active = Vec::<(libc::pid_t, Sender<ProcessExit>, u64)>::new();
+            let max = cmp::max(input, read_fd) + 1;
+
+            'outer: loop {
+                // Figure out the timeout of our syscall-to-happen. If we're waiting
+                // for some processes, then they'll have a timeout, otherwise we
+                // wait indefinitely for a message to arrive.
+                //
+                // FIXME: sure would be nice to not have to scan the entire array
+                let min = active.iter().map(|a| *a.ref2()).enumerate().min_by(|p| {
+                    p.val1()
+                });
+                let (p, idx) = match min {
+                    Some((idx, deadline)) => {
+                        let now = sys::timer::now();
+                        let ms = if now < deadline {deadline - now} else {0};
+                        tv = ms_to_timeval(ms);
+                        (&mut tv as *mut _, idx)
+                    }
+                    None => (ptr::null_mut(), -1),
+                };
+
+                // Wait for something to happen
+                c::fd_set(&mut set, input);
+                c::fd_set(&mut set, read_fd);
+                match unsafe { c::select(max, &mut set, ptr::null_mut(),
+                                         ptr::null_mut(), p) } {
+                    // interrupted, retry
+                    -1 if os::errno() == libc::EINTR as uint => continue,
+
+                    // We read something, break out and process
+                    1 | 2 => {}
+
+                    // Timeout, the pending request is removed
+                    0 => {
+                        drop(active.remove(idx));
+                        continue
+                    }
+
+                    n => panic!("error in select {} ({})", os::errno(), n),
+                }
+
+                // Process any pending messages
+                if drain(input) {
+                    loop {
+                        match messages.try_recv() {
+                            Ok(NewChild(pid, tx, deadline)) => {
+                                active.push((pid, tx, deadline));
+                            }
+                            Err(comm::Disconnected) => {
+                                assert!(active.len() == 0);
+                                break 'outer;
+                            }
+                            Err(comm::Empty) => break,
+                        }
+                    }
+                }
+
+                // If a child exited (somehow received SIGCHLD), then poll all
+                // children to see if any of them exited.
+                //
+                // We also attempt to be responsible netizens when dealing with
+                // SIGCHLD by invoking any previous SIGCHLD handler instead of just
+                // ignoring any previous SIGCHLD handler. Note that we don't provide
+                // a 1:1 mapping of our handler invocations to the previous handler
+                // invocations because we drain the `read_fd` entirely. This is
+                // probably OK because the kernel is already allowed to coalesce
+                // simultaneous signals, we're just doing some extra coalescing.
+                //
+                // Another point of note is that this likely runs the signal handler
+                // on a different thread than the one that received the signal. I
+                // *think* this is ok at this time.
+                //
+                // The main reason for doing this is to allow stdtest to run native
+                // tests as well. Both libgreen and libnative are running around
+                // with process timeouts, but libgreen should get there first
+                // (currently libuv doesn't handle old signal handlers).
+                if drain(read_fd) {
+                    let i: uint = unsafe { mem::transmute(old.sa_handler) };
+                    if i != 0 {
+                        assert!(old.sa_flags & c::SA_SIGINFO == 0);
+                        (old.sa_handler)(c::SIGCHLD);
+                    }
+
+                    // FIXME: sure would be nice to not have to scan the entire
+                    //        array...
+                    active.retain(|&(pid, ref tx, _)| {
+                        let pr = Process { pid: pid };
+                        match pr.try_wait() {
+                            Some(msg) => { tx.send(msg); false }
+                            None => true,
+                        }
+                    });
+                }
+            }
+
+            // Once this helper thread is done, we re-register the old sigchld
+            // handler and close our intermediate file descriptors.
+            unsafe {
+                assert_eq!(c::sigaction(c::SIGCHLD, &old, ptr::null_mut()), 0);
+                let _ = libc::close(read_fd);
+                let _ = libc::close(WRITE_FD);
+                WRITE_FD = -1;
+            }
+        }
+
+        // Drain all pending data from the file descriptor, returning if any data
+        // could be drained. This requires that the file descriptor is in
+        // nonblocking mode.
+        fn drain(fd: libc::c_int) -> bool {
+            let mut ret = false;
+            loop {
+                let mut buf = [0u8, ..1];
+                match unsafe {
+                    libc::read(fd, buf.as_mut_ptr() as *mut libc::c_void,
+                               buf.len() as libc::size_t)
+                } {
+                    n if n > 0 => { ret = true; }
+                    0 => return true,
+                    -1 if wouldblock() => return ret,
+                    n => panic!("bad read {} ({})", os::last_os_error(), n),
+                }
+            }
+        }
+
+        // Signal handler for SIGCHLD signals, must be async-signal-safe!
+        //
+        // This function will write to the writing half of the "self pipe" to wake
+        // up the helper thread if it's waiting. Note that this write must be
+        // nonblocking because if it blocks and the reader is the thread we
+        // interrupted, then we'll deadlock.
+        //
+        // When writing, if the write returns EWOULDBLOCK then we choose to ignore
+        // it. At that point we're guaranteed that there's something in the pipe
+        // which will wake up the other end at some point, so we just allow this
+        // signal to be coalesced with the pending signals on the pipe.
+        extern fn sigchld_handler(_signum: libc::c_int) {
+            let msg = 1i;
+            match unsafe {
+                libc::write(WRITE_FD, &msg as *const _ as *const libc::c_void, 1)
+            } {
+                1 => {}
+                -1 if wouldblock() => {} // see above comments
+                n => panic!("bad error on write fd: {} {}", n, os::errno()),
+            }
+        }
+    }
+
+    pub fn try_wait(&self) -> Option<ProcessExit> {
+        let mut status = 0 as c_int;
+        match retry(|| unsafe {
+            c::waitpid(self.pid, &mut status, c::WNOHANG)
+        }) {
+            n if n == self.pid => Some(translate_status(status)),
+            0 => None,
+            n => panic!("unknown waitpid error `{}`: {}", n,
+                       super::last_error()),
+        }
+    }
+}
+
+fn with_argv<T>(prog: &CString, args: &[CString],
+                cb: proc(*const *const libc::c_char) -> T) -> T {
+    let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1);
+
+    // Convert the CStrings into an array of pointers. Note: the
+    // lifetime of the various CStrings involved is guaranteed to be
+    // larger than the lifetime of our invocation of cb, but this is
+    // technically unsafe as the callback could leak these pointers
+    // out of our scope.
+    ptrs.push(prog.as_ptr());
+    ptrs.extend(args.iter().map(|tmp| tmp.as_ptr()));
+
+    // Add a terminating null pointer (required by libc).
+    ptrs.push(ptr::null());
+
+    cb(ptrs.as_ptr())
+}
+
+fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>,
+                      cb: proc(*const c_void) -> T) -> T
+    where K: BytesContainer + Eq + Hash, V: BytesContainer
+{
+    // On posixy systems we can pass a char** for envp, which is a
+    // null-terminated array of "k=v\0" strings. Since we must create
+    // these strings locally, yet expose a raw pointer to them, we
+    // create a temporary vector to own the CStrings that outlives the
+    // call to cb.
+    match env {
+        Some(env) => {
+            let mut tmps = Vec::with_capacity(env.len());
+
+            for pair in env.iter() {
+                let mut kv = Vec::new();
+                kv.push_all(pair.ref0().container_as_bytes());
+                kv.push('=' as u8);
+                kv.push_all(pair.ref1().container_as_bytes());
+                kv.push(0); // terminating null
+                tmps.push(kv);
+            }
+
+            // As with `with_argv`, this is unsafe, since cb could leak the pointers.
+            let mut ptrs: Vec<*const libc::c_char> =
+                tmps.iter()
+                    .map(|tmp| tmp.as_ptr() as *const libc::c_char)
+                    .collect();
+            ptrs.push(ptr::null());
+
+            cb(ptrs.as_ptr() as *const c_void)
+        }
+        _ => cb(ptr::null())
+    }
+}
+
+fn translate_status(status: c_int) -> ProcessExit {
+    #![allow(non_snake_case)]
+    #[cfg(any(target_os = "linux", target_os = "android"))]
+    mod imp {
+        pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 }
+        pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff }
+        pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f }
+    }
+
+    #[cfg(any(target_os = "macos",
+              target_os = "ios",
+              target_os = "freebsd",
+              target_os = "dragonfly"))]
+    mod imp {
+        pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 }
+        pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 }
+        pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 }
+    }
+
+    if imp::WIFEXITED(status) {
+        ExitStatus(imp::WEXITSTATUS(status) as int)
+    } else {
+        ExitSignal(imp::WTERMSIG(status) as int)
+    }
+}
diff --git a/src/libstd/platform_imp/windows/fs.rs b/src/libstd/sys/windows/fs.rs
similarity index 100%
rename from src/libstd/platform_imp/windows/fs.rs
rename to src/libstd/sys/windows/fs.rs
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 6f6ca3f2e6258..f50244701e474 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -40,6 +40,7 @@ pub mod tcp;
 pub mod udp;
 pub mod pipe;
 pub mod helper_signal;
+pub mod process;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libstd/sys/windows/process.rs b/src/libstd/sys/windows/process.rs
new file mode 100644
index 0000000000000..67e87841ed24d
--- /dev/null
+++ b/src/libstd/sys/windows/process.rs
@@ -0,0 +1,511 @@
+// Copyright 2012-2014 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 libc::{pid_t, c_void, c_int};
+use libc;
+use c_str::CString;
+use io;
+use mem;
+use os;
+use ptr;
+use prelude::*;
+use io::process::{ProcessExit, ExitStatus, ExitSignal};
+use collections;
+use path::BytesContainer;
+use hash::Hash;
+use io::{IoResult, IoError};
+
+use sys::fs;
+use sys::{mod, retry, c, wouldblock, set_nonblocking, ms_to_timeval, timer};
+use sys::fs::FileDesc;
+use sys_common::helper_thread::Helper;
+use sys_common::{AsFileDesc, mkerr_libc, timeout};
+
+use io::fs::PathExtensions;
+use string::String;
+
+pub use sys_common::ProcessConfig;
+
+/**
+ * A value representing a child process.
+ *
+ * The lifetime of this value is linked to the lifetime of the actual
+ * process - the Process destructor calls self.finish() which waits
+ * for the process to terminate.
+ */
+pub struct Process {
+    /// The unique id of the process (this should never be negative).
+    pid: pid_t,
+
+    /// A HANDLE to the process, which will prevent the pid being
+    /// re-used until the handle is closed.
+    handle: *mut (),
+}
+
+impl Drop for Process {
+    fn drop(&mut self) {
+        free_handle(self.handle);
+    }
+}
+
+impl Process {
+    pub fn id(&self) -> pid_t {
+        self.pid
+    }
+
+    pub unsafe fn kill(&self, signal: int) -> IoResult<()> {
+        Process::killpid(self.pid, signal)
+    }
+
+    pub unsafe fn killpid(pid: pid_t, signal: int) -> IoResult<()> {
+        let handle = libc::OpenProcess(libc::PROCESS_TERMINATE |
+                                       libc::PROCESS_QUERY_INFORMATION,
+                                       libc::FALSE, pid as libc::DWORD);
+        if handle.is_null() {
+            return Err(super::last_error())
+        }
+        let ret = match signal {
+            // test for existence on signal 0
+            0 => {
+                let mut status = 0;
+                let ret = libc::GetExitCodeProcess(handle, &mut status);
+                if ret == 0 {
+                    Err(super::last_error())
+                } else if status != libc::STILL_ACTIVE {
+                    Err(IoError {
+                        kind: io::InvalidInput,
+                        desc: "no process to kill",
+                        detail: None,
+                    })
+                } else {
+                    Ok(())
+                }
+            }
+            15 | 9 => { // sigterm or sigkill
+                let ret = libc::TerminateProcess(handle, 1);
+                super::mkerr_winbool(ret)
+            }
+            _ => Err(IoError {
+                kind: io::IoUnavailable,
+                desc: "unsupported signal on windows",
+                detail: None,
+            })
+        };
+        let _ = libc::CloseHandle(handle);
+        return ret;
+    }
+
+    pub fn spawn<K, V, C, P>(cfg: &C, in_fd: Option<P>,
+                              out_fd: Option<P>, err_fd: Option<P>)
+                              -> IoResult<Process>
+        where C: ProcessConfig<K, V>, P: AsFileDesc,
+              K: BytesContainer + Eq + Hash, V: BytesContainer
+    {
+        use libc::types::os::arch::extra::{DWORD, HANDLE, STARTUPINFO};
+        use libc::consts::os::extra::{
+            TRUE, FALSE,
+            STARTF_USESTDHANDLES,
+            INVALID_HANDLE_VALUE,
+            DUPLICATE_SAME_ACCESS
+        };
+        use libc::funcs::extra::kernel32::{
+            GetCurrentProcess,
+            DuplicateHandle,
+            CloseHandle,
+            CreateProcessW
+        };
+        use libc::funcs::extra::msvcrt::get_osfhandle;
+
+        use mem;
+        use iter::Iterator;
+        use str::StrPrelude;
+
+        if cfg.gid().is_some() || cfg.uid().is_some() {
+            return Err(IoError {
+                kind: io::IoUnavailable,
+                desc: "unsupported gid/uid requested on windows",
+                detail: None,
+            })
+        }
+
+        // To have the spawning semantics of unix/windows stay the same, we need to
+        // read the *child's* PATH if one is provided. See #15149 for more details.
+        let program = cfg.env().and_then(|env| {
+            for (key, v) in env.iter() {
+                if b"PATH" != key.container_as_bytes() { continue }
+
+                // Split the value and test each path to see if the
+                // program exists.
+                for path in os::split_paths(v.container_as_bytes()).into_iter() {
+                    let path = path.join(cfg.program().as_bytes_no_nul())
+                                   .with_extension(os::consts::EXE_EXTENSION);
+                    if path.exists() {
+                        return Some(path.to_c_str())
+                    }
+                }
+                break
+            }
+            None
+        });
+
+        unsafe {
+            let mut si = zeroed_startupinfo();
+            si.cb = mem::size_of::<STARTUPINFO>() as DWORD;
+            si.dwFlags = STARTF_USESTDHANDLES;
+
+            let cur_proc = GetCurrentProcess();
+
+            // Similarly to unix, we don't actually leave holes for the stdio file
+            // descriptors, but rather open up /dev/null equivalents. These
+            // equivalents are drawn from libuv's windows process spawning.
+            let set_fd = |fd: &Option<P>, slot: &mut HANDLE,
+                          is_stdin: bool| {
+                match *fd {
+                    None => {
+                        let access = if is_stdin {
+                            libc::FILE_GENERIC_READ
+                        } else {
+                            libc::FILE_GENERIC_WRITE | libc::FILE_READ_ATTRIBUTES
+                        };
+                        let size = mem::size_of::<libc::SECURITY_ATTRIBUTES>();
+                        let mut sa = libc::SECURITY_ATTRIBUTES {
+                            nLength: size as libc::DWORD,
+                            lpSecurityDescriptor: ptr::null_mut(),
+                            bInheritHandle: 1,
+                        };
+                        let mut filename: Vec<u16> = "NUL".utf16_units().collect();
+                        filename.push(0);
+                        *slot = libc::CreateFileW(filename.as_ptr(),
+                                                  access,
+                                                  libc::FILE_SHARE_READ |
+                                                      libc::FILE_SHARE_WRITE,
+                                                  &mut sa,
+                                                  libc::OPEN_EXISTING,
+                                                  0,
+                                                  ptr::null_mut());
+                        if *slot == INVALID_HANDLE_VALUE {
+                            return Err(super::last_error())
+                        }
+                    }
+                    Some(ref fd) => {
+                        let orig = get_osfhandle(fd.as_fd().fd()) as HANDLE;
+                        if orig == INVALID_HANDLE_VALUE {
+                            return Err(super::last_error())
+                        }
+                        if DuplicateHandle(cur_proc, orig, cur_proc, slot,
+                                           0, TRUE, DUPLICATE_SAME_ACCESS) == FALSE {
+                            return Err(super::last_error())
+                        }
+                    }
+                }
+                Ok(())
+            };
+
+            try!(set_fd(&in_fd, &mut si.hStdInput, true));
+            try!(set_fd(&out_fd, &mut si.hStdOutput, false));
+            try!(set_fd(&err_fd, &mut si.hStdError, false));
+
+            let cmd_str = make_command_line(program.as_ref().unwrap_or(cfg.program()),
+                                            cfg.args());
+            let mut pi = zeroed_process_information();
+            let mut create_err = None;
+
+            // stolen from the libuv code.
+            let mut flags = libc::CREATE_UNICODE_ENVIRONMENT;
+            if cfg.detach() {
+                flags |= libc::DETACHED_PROCESS | libc::CREATE_NEW_PROCESS_GROUP;
+            }
+
+            with_envp(cfg.env(), |envp| {
+                with_dirp(cfg.cwd(), |dirp| {
+                    let mut cmd_str: Vec<u16> = cmd_str.as_slice().utf16_units().collect();
+                    cmd_str.push(0);
+                    let created = CreateProcessW(ptr::null(),
+                                                 cmd_str.as_mut_ptr(),
+                                                 ptr::null_mut(),
+                                                 ptr::null_mut(),
+                                                 TRUE,
+                                                 flags, envp, dirp,
+                                                 &mut si, &mut pi);
+                    if created == FALSE {
+                        create_err = Some(super::last_error());
+                    }
+                })
+            });
+
+            assert!(CloseHandle(si.hStdInput) != 0);
+            assert!(CloseHandle(si.hStdOutput) != 0);
+            assert!(CloseHandle(si.hStdError) != 0);
+
+            match create_err {
+                Some(err) => return Err(err),
+                None => {}
+            }
+
+            // We close the thread handle because we don't care about keeping the
+            // thread id valid, and we aren't keeping the thread handle around to be
+            // able to close it later. We don't close the process handle however
+            // because std::we want the process id to stay valid at least until the
+            // calling code closes the process handle.
+            assert!(CloseHandle(pi.hThread) != 0);
+
+            Ok(Process {
+                pid: pi.dwProcessId as pid_t,
+                handle: pi.hProcess as *mut ()
+            })
+        }
+    }
+
+    /**
+     * Waits for a process to exit and returns the exit code, failing
+     * if there is no process with the specified id.
+     *
+     * Note that this is private to avoid race conditions on unix where if
+     * a user calls waitpid(some_process.get_id()) then some_process.finish()
+     * and some_process.destroy() and some_process.finalize() will then either
+     * operate on a none-existent process or, even worse, on a newer process
+     * with the same id.
+     */
+    pub fn wait(&self, deadline: u64) -> IoResult<ProcessExit> {
+        use libc::types::os::arch::extra::DWORD;
+        use libc::consts::os::extra::{
+            SYNCHRONIZE,
+            PROCESS_QUERY_INFORMATION,
+            FALSE,
+            STILL_ACTIVE,
+            INFINITE,
+            WAIT_TIMEOUT,
+            WAIT_OBJECT_0,
+        };
+        use libc::funcs::extra::kernel32::{
+            OpenProcess,
+            GetExitCodeProcess,
+            CloseHandle,
+            WaitForSingleObject,
+        };
+
+        unsafe {
+            let process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION,
+                                      FALSE,
+                                      self.pid as DWORD);
+            if process.is_null() {
+                return Err(super::last_error())
+            }
+
+            loop {
+                let mut status = 0;
+                if GetExitCodeProcess(process, &mut status) == FALSE {
+                    let err = Err(super::last_error());
+                    assert!(CloseHandle(process) != 0);
+                    return err;
+                }
+                if status != STILL_ACTIVE {
+                    assert!(CloseHandle(process) != 0);
+                    return Ok(ExitStatus(status as int));
+                }
+                let interval = if deadline == 0 {
+                    INFINITE
+                } else {
+                    let now = timer::now();
+                    if deadline < now {0} else {(deadline - now) as u32}
+                };
+                match WaitForSingleObject(process, interval) {
+                    WAIT_OBJECT_0 => {}
+                    WAIT_TIMEOUT => {
+                        assert!(CloseHandle(process) != 0);
+                        return Err(timeout("process wait timed out"))
+                    }
+                    _ => {
+                        let err = Err(super::last_error());
+                        assert!(CloseHandle(process) != 0);
+                        return err
+                    }
+                }
+            }
+        }
+    }
+}
+
+fn zeroed_startupinfo() -> libc::types::os::arch::extra::STARTUPINFO {
+    libc::types::os::arch::extra::STARTUPINFO {
+        cb: 0,
+        lpReserved: ptr::null_mut(),
+        lpDesktop: ptr::null_mut(),
+        lpTitle: ptr::null_mut(),
+        dwX: 0,
+        dwY: 0,
+        dwXSize: 0,
+        dwYSize: 0,
+        dwXCountChars: 0,
+        dwYCountCharts: 0,
+        dwFillAttribute: 0,
+        dwFlags: 0,
+        wShowWindow: 0,
+        cbReserved2: 0,
+        lpReserved2: ptr::null_mut(),
+        hStdInput: libc::INVALID_HANDLE_VALUE,
+        hStdOutput: libc::INVALID_HANDLE_VALUE,
+        hStdError: libc::INVALID_HANDLE_VALUE,
+    }
+}
+
+fn zeroed_process_information() -> libc::types::os::arch::extra::PROCESS_INFORMATION {
+    libc::types::os::arch::extra::PROCESS_INFORMATION {
+        hProcess: ptr::null_mut(),
+        hThread: ptr::null_mut(),
+        dwProcessId: 0,
+        dwThreadId: 0
+    }
+}
+
+fn make_command_line(prog: &CString, args: &[CString]) -> String {
+    let mut cmd = String::new();
+    append_arg(&mut cmd, prog.as_str()
+                             .expect("expected program name to be utf-8 encoded"));
+    for arg in args.iter() {
+        cmd.push(' ');
+        append_arg(&mut cmd, arg.as_str()
+                                .expect("expected argument to be utf-8 encoded"));
+    }
+    return cmd;
+
+    fn append_arg(cmd: &mut String, arg: &str) {
+        // If an argument has 0 characters then we need to quote it to ensure
+        // that it actually gets passed through on the command line or otherwise
+        // it will be dropped entirely when parsed on the other end.
+        let quote = arg.chars().any(|c| c == ' ' || c == '\t') || arg.len() == 0;
+        if quote {
+            cmd.push('"');
+        }
+        let argvec: Vec<char> = arg.chars().collect();
+        for i in range(0u, argvec.len()) {
+            append_char_at(cmd, argvec.as_slice(), i);
+        }
+        if quote {
+            cmd.push('"');
+        }
+    }
+
+    fn append_char_at(cmd: &mut String, arg: &[char], i: uint) {
+        match arg[i] {
+            '"' => {
+                // Escape quotes.
+                cmd.push_str("\\\"");
+            }
+            '\\' => {
+                if backslash_run_ends_in_quote(arg, i) {
+                    // Double all backslashes that are in runs before quotes.
+                    cmd.push_str("\\\\");
+                } else {
+                    // Pass other backslashes through unescaped.
+                    cmd.push('\\');
+                }
+            }
+            c => {
+                cmd.push(c);
+            }
+        }
+    }
+
+    fn backslash_run_ends_in_quote(s: &[char], mut i: uint) -> bool {
+        while i < s.len() && s[i] == '\\' {
+            i += 1;
+        }
+        return i < s.len() && s[i] == '"';
+    }
+}
+
+fn with_envp<K, V, T>(env: Option<&collections::HashMap<K, V>>,
+                      cb: |*mut c_void| -> T) -> T
+    where K: BytesContainer + Eq + Hash, V: BytesContainer
+{
+    // On Windows we pass an "environment block" which is not a char**, but
+    // rather a concatenation of null-terminated k=v\0 sequences, with a final
+    // \0 to terminate.
+    match env {
+        Some(env) => {
+            let mut blk = Vec::new();
+
+            for pair in env.iter() {
+                let kv = format!("{}={}",
+                                 pair.ref0().container_as_str().unwrap(),
+                                 pair.ref1().container_as_str().unwrap());
+                blk.extend(kv.as_slice().utf16_units());
+                blk.push(0);
+            }
+
+            blk.push(0);
+
+            cb(blk.as_mut_ptr() as *mut c_void)
+        }
+        _ => cb(ptr::null_mut())
+    }
+}
+
+fn with_dirp<T>(d: Option<&CString>, cb: |*const u16| -> T) -> T {
+    match d {
+      Some(dir) => {
+          let dir_str = dir.as_str()
+                           .expect("expected workingdirectory to be utf-8 encoded");
+          let mut dir_str: Vec<u16> = dir_str.utf16_units().collect();
+          dir_str.push(0);
+          cb(dir_str.as_ptr())
+      },
+      None => cb(ptr::null())
+    }
+}
+
+fn free_handle(handle: *mut ()) {
+    assert!(unsafe {
+        libc::CloseHandle(mem::transmute(handle)) != 0
+    })
+}
+
+#[cfg(test)]
+mod tests {
+
+    #[test]
+    fn test_make_command_line() {
+        use prelude::*;
+        use str;
+        use c_str::CString;
+        use super::make_command_line;
+
+        fn test_wrapper(prog: &str, args: &[&str]) -> String {
+            make_command_line(&prog.to_c_str(),
+                              args.iter()
+                                  .map(|a| a.to_c_str())
+                                  .collect::<Vec<CString>>()
+                                  .as_slice())
+        }
+
+        assert_eq!(
+            test_wrapper("prog", ["aaa", "bbb", "ccc"]),
+            "prog aaa bbb ccc".to_string()
+        );
+
+        assert_eq!(
+            test_wrapper("C:\\Program Files\\blah\\blah.exe", ["aaa"]),
+            "\"C:\\Program Files\\blah\\blah.exe\" aaa".to_string()
+        );
+        assert_eq!(
+            test_wrapper("C:\\Program Files\\test", ["aa\"bb"]),
+            "\"C:\\Program Files\\test\" aa\\\"bb".to_string()
+        );
+        assert_eq!(
+            test_wrapper("echo", ["a b c"]),
+            "echo \"a b c\"".to_string()
+        );
+        assert_eq!(
+            test_wrapper("\u03c0\u042f\u97f3\u00e6\u221e", []),
+            "\u03c0\u042f\u97f3\u00e6\u221e".to_string()
+        );
+    }
+}

From b8f1193bb1bb66610f479cd78e3dc5526e93058d Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Thu, 16 Oct 2014 18:57:11 -0700
Subject: [PATCH 07/11] Runtime removal: refactor timer

This patch continues runtime removal by moving out timer-related code
into `sys`.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/mod.rs                       | 17 -----
 src/libstd/io/timer.rs                        | 19 ++---
 src/libstd/sys/unix/mod.rs                    |  1 +
 .../sys/unix/timer.rs}                        | 75 +++++++++----------
 src/libstd/sys/windows/mod.rs                 |  1 +
 .../sys/windows/timer.rs}                     | 30 ++++----
 6 files changed, 60 insertions(+), 83 deletions(-)
 rename src/{libnative/io/timer_unix.rs => libstd/sys/unix/timer.rs} (92%)
 rename src/{libnative/io/timer_windows.rs => libstd/sys/windows/timer.rs} (93%)

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 29370dee88b4d..5d6d23f5f03ab 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -29,19 +29,6 @@ use std::os;
 use std::rt::rtio::{mod, IoResult, IoError};
 use std::num;
 
-#[cfg(any(target_os = "macos",
-          target_os = "ios",
-          target_os = "freebsd",
-          target_os = "dragonfly",
-          target_os = "android",
-          target_os = "linux"))]
-#[path = "timer_unix.rs"]
-pub mod timer;
-
-#[cfg(target_os = "windows")]
-#[path = "timer_windows.rs"]
-pub mod timer;
-
 #[cfg(windows)]
 #[path = "tty_windows.rs"]
 mod tty;
@@ -112,10 +99,6 @@ impl IoFactory {
 }
 
 impl rtio::IoFactory for IoFactory {
-    // misc
-    fn timer_init(&mut self) -> IoResult<Box<rtio::RtioTimer + Send>> {
-        timer::Timer::new().map(|t| box t as Box<rtio::RtioTimer + Send>)
-    }
     #[cfg(unix)]
     fn tty_open(&mut self, fd: c_int, _readable: bool)
                 -> IoResult<Box<rtio::RtioTTY + Send>> {
diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs
index d16199da77fcd..ec588f134784a 100644
--- a/src/libstd/io/timer.rs
+++ b/src/libstd/io/timer.rs
@@ -21,10 +21,9 @@ and create receivers which will receive notifications after a period of time.
 
 use comm::{Receiver, Sender, channel};
 use time::Duration;
-use io::{IoResult, IoError};
-use kinds::Send;
-use boxed::Box;
-use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
+use io::IoResult;
+use sys::timer::Callback;
+use sys::timer::Timer as TimerImp;
 
 /// A synchronous timer object
 ///
@@ -69,7 +68,7 @@ use rt::rtio::{IoFactory, LocalIo, RtioTimer, Callback};
 /// # }
 /// ```
 pub struct Timer {
-    obj: Box<RtioTimer + Send>,
+    inner: TimerImp,
 }
 
 struct TimerCallback { tx: Sender<()> }
@@ -90,9 +89,7 @@ impl Timer {
     /// for a number of milliseconds, or to possibly create channels which will
     /// get notified after an amount of time has passed.
     pub fn new() -> IoResult<Timer> {
-        LocalIo::maybe_raise(|io| {
-            io.timer_init().map(|t| Timer { obj: t })
-        }).map_err(IoError::from_rtio_error)
+        TimerImp::new().map(|t| Timer { inner: t })
     }
 
     /// Blocks the current task for the specified duration.
@@ -106,7 +103,7 @@ impl Timer {
         // Short-circuit the timer backend for 0 duration
         let ms = in_ms_u64(duration);
         if ms == 0 { return }
-        self.obj.sleep(ms);
+        self.inner.sleep(ms);
     }
 
     /// Creates a oneshot receiver which will have a notification sent when
@@ -152,7 +149,7 @@ impl Timer {
         let (tx, rx) = channel();
         // Short-circuit the timer backend for 0 duration
         if in_ms_u64(duration) != 0 {
-            self.obj.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
+            self.inner.oneshot(in_ms_u64(duration), box TimerCallback { tx: tx });
         } else {
             tx.send(());
         }
@@ -213,7 +210,7 @@ impl Timer {
         // not clear what use a 0ms period is anyway...
         let ms = if ms == 0 { 1 } else { ms };
         let (tx, rx) = channel();
-        self.obj.period(ms, box TimerCallback { tx: tx });
+        self.inner.period(ms, box TimerCallback { tx: tx });
         return rx
     }
 }
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index b404dc7fdbd29..03a4e56f00dc4 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -34,6 +34,7 @@ pub mod udp;
 pub mod pipe;
 pub mod helper_signal;
 pub mod process;
+pub mod timer;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libnative/io/timer_unix.rs b/src/libstd/sys/unix/timer.rs
similarity index 92%
rename from src/libnative/io/timer_unix.rs
rename to src/libstd/sys/unix/timer.rs
index c26e2e76cee63..a1e6ac3db7e38 100644
--- a/src/libnative/io/timer_unix.rs
+++ b/src/libstd/sys/unix/timer.rs
@@ -47,27 +47,30 @@
 //! Note that all time units in this file are in *milliseconds*.
 
 use libc;
-use std::mem;
-use std::os;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::IoResult;
-use std::sync::atomic;
-use std::comm;
-
-use io::c;
-use platform_imp::fs::FileDesc;
-use io::helper_thread::Helper;
+use mem;
+use os;
+use ptr;
+use sync::atomic;
+use comm;
+use sys::c;
+use sys::fs::FileDesc;
+use sys_common::helper_thread::Helper;
+use prelude::*;
+use io::IoResult;
 
 helper_init!(static HELPER: Helper<Req>)
 
+pub trait Callback {
+    fn call(&mut self);
+}
+
 pub struct Timer {
     id: uint,
     inner: Option<Box<Inner>>,
 }
 
 pub struct Inner {
-    cb: Option<Box<rtio::Callback + Send>>,
+    cb: Option<Box<Callback + Send>>,
     interval: u64,
     repeat: bool,
     target: u64,
@@ -190,11 +193,11 @@ fn helper(input: libc::c_int, messages: Receiver<Req>, _: ()) {
 
                 // drain the file descriptor
                 let mut buf = [0];
-                assert_eq!(fd.inner_read(buf).ok().unwrap(), 1);
+                assert_eq!(fd.read(buf).ok().unwrap(), 1);
             }
 
-            -1 if os::errno() == libc::EINTR as int => {}
-            n => panic!("helper thread panicked in select() with error: {} ({})",
+            -1 if os::errno() == libc::EINTR as uint => {}
+            n => panic!("helper thread failed in select() with error: {} ({})",
                        n, os::last_os_error())
         }
     }
@@ -220,7 +223,11 @@ impl Timer {
         })
     }
 
-    pub fn sleep(ms: u64) {
+    pub fn sleep(&mut self, ms: u64) {
+        let mut inner = self.inner();
+        inner.cb = None; // cancel any previous request
+        self.inner = Some(inner);
+
         let mut to_sleep = libc::timespec {
             tv_sec: (ms / 1000) as libc::time_t,
             tv_nsec: ((ms % 1000) * 1000000) as libc::c_long,
@@ -232,28 +239,7 @@ impl Timer {
         }
     }
 
-    fn inner(&mut self) -> Box<Inner> {
-        match self.inner.take() {
-            Some(i) => i,
-            None => {
-                let (tx, rx) = channel();
-                HELPER.send(RemoveTimer(self.id, tx));
-                rx.recv()
-            }
-        }
-    }
-}
-
-impl rtio::RtioTimer for Timer {
-    fn sleep(&mut self, msecs: u64) {
-        let mut inner = self.inner();
-        inner.cb = None; // cancel any previous request
-        self.inner = Some(inner);
-
-        Timer::sleep(msecs);
-    }
-
-    fn oneshot(&mut self, msecs: u64, cb: Box<rtio::Callback + Send>) {
+    pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
         let now = now();
         let mut inner = self.inner();
 
@@ -265,7 +251,7 @@ impl rtio::RtioTimer for Timer {
         HELPER.send(NewTimer(inner));
     }
 
-    fn period(&mut self, msecs: u64, cb: Box<rtio::Callback + Send>) {
+    pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
         let now = now();
         let mut inner = self.inner();
 
@@ -276,6 +262,17 @@ impl rtio::RtioTimer for Timer {
 
         HELPER.send(NewTimer(inner));
     }
+
+    fn inner(&mut self) -> Box<Inner> {
+        match self.inner.take() {
+            Some(i) => i,
+            None => {
+                let (tx, rx) = channel();
+                HELPER.send(RemoveTimer(self.id, tx));
+                rx.recv()
+            }
+        }
+    }
 }
 
 impl Drop for Timer {
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index f50244701e474..0dc06de33e0cc 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -41,6 +41,7 @@ pub mod udp;
 pub mod pipe;
 pub mod helper_signal;
 pub mod process;
+pub mod timer;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libnative/io/timer_windows.rs b/src/libstd/sys/windows/timer.rs
similarity index 93%
rename from src/libnative/io/timer_windows.rs
rename to src/libstd/sys/windows/timer.rs
index c17c541fc01e6..f507be2a985df 100644
--- a/src/libnative/io/timer_windows.rs
+++ b/src/libstd/sys/windows/timer.rs
@@ -21,15 +21,21 @@
 //! the other two implementations of timers with nothing *that* new showing up.
 
 use libc;
-use std::ptr;
-use std::rt::rtio;
-use std::rt::rtio::{IoResult, Callback};
-use std::comm;
+use ptr;
+use comm;
 
-use io::helper_thread::Helper;
+use sys::c;
+use sys::fs::FileDesc;
+use sys_common::helper_thread::Helper;
+use prelude::*;
+use io::IoResult;
 
 helper_init!(static HELPER: Helper<Req>)
 
+pub trait Callback {
+    fn call(&mut self);
+}
+
 pub struct Timer {
     obj: libc::HANDLE,
     on_worker: bool,
@@ -116,12 +122,6 @@ impl Timer {
         }
     }
 
-    pub fn sleep(ms: u64) {
-        use std::rt::rtio::RtioTimer;
-        let mut t = Timer::new().ok().expect("must allocate a timer!");
-        t.sleep(ms);
-    }
-
     fn remove(&mut self) {
         if !self.on_worker { return }
 
@@ -131,10 +131,8 @@ impl Timer {
 
         self.on_worker = false;
     }
-}
 
-impl rtio::RtioTimer for Timer {
-    fn sleep(&mut self, msecs: u64) {
+    pub fn sleep(&mut self, msecs: u64) {
         self.remove();
 
         // there are 10^6 nanoseconds in a millisecond, and the parameter is in
@@ -148,7 +146,7 @@ impl rtio::RtioTimer for Timer {
         let _ = unsafe { imp::WaitForSingleObject(self.obj, libc::INFINITE) };
     }
 
-    fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
+    pub fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>) {
         self.remove();
 
         // see above for the calculation
@@ -162,7 +160,7 @@ impl rtio::RtioTimer for Timer {
         self.on_worker = true;
     }
 
-    fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
+    pub fn period(&mut self, msecs: u64, cb: Box<Callback + Send>) {
         self.remove();
 
         // see above for the calculation

From 431dcdc840a27f7c7418b7dff73a329eada8a407 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Fri, 17 Oct 2014 13:33:08 -0700
Subject: [PATCH 08/11] Runtime removal: refactor tty

This patch continues runtime removal by moving the tty implementations
into `sys`.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libnative/io/mod.rs                       | 26 ------
 src/libstd/io/stdio.rs                        | 26 +++---
 src/libstd/sys/common/mod.rs                  |  8 ++
 src/libstd/sys/unix/fs.rs                     | 19 -----
 src/libstd/sys/unix/mod.rs                    |  1 +
 src/libstd/sys/unix/tty.rs                    | 47 +++++++++++
 src/libstd/sys/windows/mod.rs                 |  1 +
 .../sys/windows/tty.rs}                       | 80 ++++++++++---------
 8 files changed, 112 insertions(+), 96 deletions(-)
 create mode 100644 src/libstd/sys/unix/tty.rs
 rename src/{libnative/io/tty_windows.rs => libstd/sys/windows/tty.rs} (79%)

diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
index 5d6d23f5f03ab..8c7751588cef3 100644
--- a/src/libnative/io/mod.rs
+++ b/src/libnative/io/mod.rs
@@ -99,30 +99,4 @@ impl IoFactory {
 }
 
 impl rtio::IoFactory for IoFactory {
-    #[cfg(unix)]
-    fn tty_open(&mut self, fd: c_int, _readable: bool)
-                -> IoResult<Box<rtio::RtioTTY + Send>> {
-        if unsafe { libc::isatty(fd) } != 0 {
-            Ok(box file::FileDesc::new(fd, true) as Box<rtio::RtioTTY + Send>)
-        } else {
-            Err(IoError {
-                code: libc::ENOTTY as uint,
-                extra: 0,
-                detail: None,
-            })
-        }
-    }
-    #[cfg(windows)]
-    fn tty_open(&mut self, fd: c_int, _readable: bool)
-                -> IoResult<Box<rtio::RtioTTY + Send>> {
-        if tty::is_tty(fd) {
-            Ok(box tty::WindowsTTY::new(fd) as Box<rtio::RtioTTY + Send>)
-        } else {
-            Err(IoError {
-                code: libc::ERROR_INVALID_HANDLE as uint,
-                extra: 0,
-                detail: None,
-            })
-        }
-    }
 }
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 98644cfc7e995..158d596ea136b 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -36,7 +36,7 @@ use kinds::Send;
 use libc;
 use option::{Option, Some, None};
 use boxed::Box;
-use sys::fs::FileDesc;
+use sys::{fs, tty};
 use result::{Ok, Err};
 use rt;
 use rt::local::Local;
@@ -74,17 +74,15 @@ use uint;
 // tl;dr; TTY works on everything but when windows stdout is redirected, in that
 //        case pipe also doesn't work, but magically file does!
 enum StdSource {
-    TTY(Box<RtioTTY + Send>),
-    File(FileDesc),
+    TTY(tty::TTY),
+    File(fs::FileDesc),
 }
 
-fn src<T>(fd: libc::c_int, readable: bool, f: |StdSource| -> T) -> T {
-    LocalIo::maybe_raise(|io| {
-        Ok(match io.tty_open(fd, readable) {
-            Ok(tty) => f(TTY(tty)),
-            Err(_) => f(File(FileDesc::new(fd, false))),
-        })
-    }).map_err(IoError::from_rtio_error).unwrap()
+fn src<T>(fd: libc::c_int, _readable: bool, f: |StdSource| -> T) -> T {
+    match tty::TTY::new(fd) {
+        Ok(tty) => f(TTY(tty)),
+        Err(_) => f(File(fs::FileDesc::new(fd, false))),
+    }
 }
 
 local_data_key!(local_stdout: Box<Writer + Send>)
@@ -278,7 +276,7 @@ impl Reader for StdReader {
                 // print!'d prompt not being shown until after the user hits
                 // enter.
                 flush();
-                tty.read(buf).map_err(IoError::from_rtio_error)
+                tty.read(buf).map(|i| i as uint)
             },
             File(ref mut file) => file.read(buf).map(|i| i as uint),
         };
@@ -313,7 +311,7 @@ impl StdWriter {
     pub fn winsize(&mut self) -> IoResult<(int, int)> {
         match self.inner {
             TTY(ref mut tty) => {
-                tty.get_winsize().map_err(IoError::from_rtio_error)
+                tty.get_winsize()
             }
             File(..) => {
                 Err(IoError {
@@ -335,7 +333,7 @@ impl StdWriter {
     pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
         match self.inner {
             TTY(ref mut tty) => {
-                tty.set_raw(raw).map_err(IoError::from_rtio_error)
+                tty.set_raw(raw)
             }
             File(..) => {
                 Err(IoError {
@@ -372,7 +370,7 @@ impl Writer for StdWriter {
         let max_size = if cfg!(windows) {8192} else {uint::MAX};
         for chunk in buf.chunks(max_size) {
             try!(match self.inner {
-                TTY(ref mut tty) => tty.write(chunk).map_err(IoError::from_rtio_error),
+                TTY(ref mut tty) => tty.write(chunk),
                 File(ref mut file) => file.write(chunk),
             })
         }
diff --git a/src/libstd/sys/common/mod.rs b/src/libstd/sys/common/mod.rs
index 75c2987078dc3..c5f8214a5c38c 100644
--- a/src/libstd/sys/common/mod.rs
+++ b/src/libstd/sys/common/mod.rs
@@ -48,6 +48,14 @@ pub fn short_write(n: uint, desc: &'static str) -> IoError {
     }
 }
 
+pub fn unimpl() -> IoError {
+    IoError {
+        kind: io::IoUnavailable,
+        desc: "operations not yet supported",
+        detail: None,
+    }
+}
+
 // unix has nonzero values as errors
 pub fn mkerr_libc<Int: num::Zero>(ret: Int) -> IoResult<()> {
     if !ret.is_zero() {
diff --git a/src/libstd/sys/unix/fs.rs b/src/libstd/sys/unix/fs.rs
index 3dcd99859e8cf..2d02c34e958c6 100644
--- a/src/libstd/sys/unix/fs.rs
+++ b/src/libstd/sys/unix/fs.rs
@@ -137,25 +137,6 @@ impl FileDesc {
     }
 }
 
-/*
-
-impl RtioTTY for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
-        self.inner_read(buf)
-    }
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
-        self.inner_write(buf)
-    }
-    fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
-        Err(super::unimpl())
-    }
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
-        Err(super::unimpl())
-    }
-    fn isatty(&self) -> bool { false }
-}
-*/
-
 impl Drop for FileDesc {
     fn drop(&mut self) {
         // closing stdio file handles makes no sense, so never do it. Also, note
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 03a4e56f00dc4..4bd1dd2016328 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -35,6 +35,7 @@ pub mod pipe;
 pub mod helper_signal;
 pub mod process;
 pub mod timer;
+pub mod tty;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libstd/sys/unix/tty.rs b/src/libstd/sys/unix/tty.rs
new file mode 100644
index 0000000000000..28c17fd4966c0
--- /dev/null
+++ b/src/libstd/sys/unix/tty.rs
@@ -0,0 +1,47 @@
+// Copyright 2014 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 sys::fs::FileDesc;
+use prelude::*;
+use libc::{mod, c_int};
+use io::{mod, IoResult, IoError};
+use sys_common;
+
+pub struct TTY {
+    pub fd: FileDesc,
+}
+
+impl TTY {
+    pub fn new(fd: c_int) -> IoResult<TTY> {
+        if unsafe { libc::isatty(fd) } != 0 {
+            Ok(TTY { fd: FileDesc::new(fd, true) })
+        } else {
+            Err(IoError {
+                kind: io::MismatchedFileTypeForOperation,
+                desc: "file descriptor is not a TTY",
+                detail: None,
+            })
+        }
+    }
+
+    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+        self.fd.read(buf)
+    }
+    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+        self.fd.write(buf)
+    }
+    pub fn set_raw(&mut self, _raw: bool) -> IoResult<()> {
+        Err(sys_common::unimpl())
+    }
+    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
+        Err(sys_common::unimpl())
+    }
+    pub fn isatty(&self) -> bool { false }
+}
diff --git a/src/libstd/sys/windows/mod.rs b/src/libstd/sys/windows/mod.rs
index 0dc06de33e0cc..98da4d4e7633d 100644
--- a/src/libstd/sys/windows/mod.rs
+++ b/src/libstd/sys/windows/mod.rs
@@ -42,6 +42,7 @@ pub mod pipe;
 pub mod helper_signal;
 pub mod process;
 pub mod timer;
+pub mod tty;
 
 pub mod addrinfo {
     pub use sys_common::net::get_host_addresses;
diff --git a/src/libnative/io/tty_windows.rs b/src/libstd/sys/windows/tty.rs
similarity index 79%
rename from src/libnative/io/tty_windows.rs
rename to src/libstd/sys/windows/tty.rs
index cf2a0f9dda444..7d001e6394c30 100644
--- a/src/libnative/io/tty_windows.rs
+++ b/src/libstd/sys/windows/tty.rs
@@ -33,16 +33,16 @@ use super::c::{ENABLE_PROCESSED_INPUT, ENABLE_QUICK_EDIT_MODE};
 use libc::{c_int, HANDLE, LPDWORD, DWORD, LPVOID};
 use libc::{get_osfhandle, CloseHandle};
 use libc::types::os::arch::extra::LPCVOID;
-use std::io::MemReader;
-use std::ptr;
-use std::rt::rtio::{IoResult, IoError, RtioTTY};
-use std::str::from_utf8;
+use io::{mod, IoError, IoResult, MemReader};
+use prelude::*;
+use ptr;
+use str::from_utf8;
 
 fn invalid_encoding() -> IoError {
     IoError {
-        code: ERROR_ILLEGAL_CHARACTER as uint,
-        extra: 0,
-        detail: Some("text was not valid unicode".to_string()),
+        kind: io::InvalidInput,
+        desc: "text was not valid unicode",
+        detail: None,
     }
 }
 
@@ -56,40 +56,37 @@ pub fn is_tty(fd: c_int) -> bool {
     }
 }
 
-pub struct WindowsTTY {
+pub struct TTY {
     closeme: bool,
     handle: HANDLE,
     utf8: MemReader,
 }
 
-impl WindowsTTY {
-    pub fn new(fd: c_int) -> WindowsTTY {
-        // If the file descriptor is one of stdin, stderr, or stdout
-        // then it should not be closed by us
-        let closeme = match fd {
-            0...2 => false,
-            _ => true,
-        };
-        let handle = unsafe { get_osfhandle(fd) as HANDLE };
-        WindowsTTY {
-            handle: handle,
-            utf8: MemReader::new(Vec::new()),
-            closeme: closeme,
+impl TTY {
+    pub fn new(fd: c_int) -> IoResult<TTY> {
+        if is_tty(fd) {
+            // If the file descriptor is one of stdin, stderr, or stdout
+            // then it should not be closed by us
+            let closeme = match fd {
+                0...2 => false,
+                _ => true,
+            };
+            let handle = unsafe { get_osfhandle(fd) as HANDLE };
+            Ok(TTY {
+                handle: handle,
+                utf8: MemReader::new(Vec::new()),
+                closeme: closeme,
+            })
+        } else {
+            Err(IoError {
+                kind: io::MismatchedFileTypeForOperation,
+                desc: "invalid handle provided to function",
+                detail: None,
+            })
         }
     }
-}
 
-impl Drop for WindowsTTY {
-    fn drop(&mut self) {
-        if self.closeme {
-            // Nobody cares about the return value
-            let _ = unsafe { CloseHandle(self.handle) };
-        }
-    }
-}
-
-impl RtioTTY for WindowsTTY {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
+    pub fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
         // Read more if the buffer is empty
         if self.utf8.eof() {
             let mut utf16 = Vec::from_elem(0x1000, 0u16);
@@ -113,7 +110,7 @@ impl RtioTTY for WindowsTTY {
         Ok(self.utf8.read(buf).unwrap())
     }
 
-    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
+    pub fn write(&mut self, buf: &[u8]) -> IoResult<()> {
         let utf16 = match from_utf8(buf) {
             Some(utf8) => {
                 utf8.as_slice().utf16_units().collect::<Vec<u16>>()
@@ -131,7 +128,7 @@ impl RtioTTY for WindowsTTY {
         }
     }
 
-    fn set_raw(&mut self, raw: bool) -> IoResult<()> {
+    pub fn set_raw(&mut self, raw: bool) -> IoResult<()> {
         // FIXME
         // Somebody needs to decide on which of these flags we want
         match unsafe { SetConsoleMode(self.handle,
@@ -146,7 +143,7 @@ impl RtioTTY for WindowsTTY {
         }
     }
 
-    fn get_winsize(&mut self) -> IoResult<(int, int)> {
+    pub fn get_winsize(&mut self) -> IoResult<(int, int)> {
         // FIXME
         // Get console buffer via CreateFile with CONOUT$
         // Make a CONSOLE_SCREEN_BUFFER_INFO
@@ -156,5 +153,14 @@ impl RtioTTY for WindowsTTY {
     }
 
     // Let us magically declare this as a TTY
-    fn isatty(&self) -> bool { true }
+    pub fn isatty(&self) -> bool { true }
+}
+
+impl Drop for TTY {
+    fn drop(&mut self) {
+        if self.closeme {
+            // Nobody cares about the return value
+            let _ = unsafe { CloseHandle(self.handle) };
+        }
+    }
 }

From fa94fdad3e880d2d6cbd82c12bd12caefbeb81a8 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Fri, 17 Oct 2014 13:39:27 -0700
Subject: [PATCH 09/11] Runtime removal: fully remove rtio

This patch cleans up the remnants of the runtime IO interface.

Because this eliminates APIs in `libnative` and `librustrt`, it is a:

[breaking-change]

This functionality is likely to be available publicly, in some form,
from `std` in the future.
---
 src/libgreen/basic.rs      |   4 +-
 src/libgreen/simple.rs     |   4 +-
 src/libgreen/task.rs       |   9 ---
 src/libnative/io/mod.rs    | 102 -----------------------------
 src/libnative/lib.rs       |   1 -
 src/libnative/task.rs      |   8 ---
 src/librustrt/lib.rs       |   1 -
 src/librustrt/rtio.rs      | 130 +------------------------------------
 src/librustrt/task.rs      |   8 ---
 src/libstd/io/mod.rs       |  15 +----
 src/libstd/sys/unix/mod.rs |   5 ++
 11 files changed, 11 insertions(+), 276 deletions(-)
 delete mode 100644 src/libnative/io/mod.rs

diff --git a/src/libgreen/basic.rs b/src/libgreen/basic.rs
index b476f46833bdb..e2b8eb54ac3ae 100644
--- a/src/libgreen/basic.rs
+++ b/src/libgreen/basic.rs
@@ -18,7 +18,7 @@
 use alloc::arc::Arc;
 use std::sync::atomic;
 use std::mem;
-use std::rt::rtio::{EventLoop, IoFactory, RemoteCallback};
+use std::rt::rtio::{EventLoop, RemoteCallback};
 use std::rt::rtio::{PausableIdleCallback, Callback};
 use std::rt::exclusive::Exclusive;
 
@@ -150,8 +150,6 @@ impl EventLoop for BasicLoop {
             Box<RemoteCallback + Send>
     }
 
-    fn io<'a>(&'a mut self) -> Option<&'a mut IoFactory> { None }
-
     fn has_active_io(&self) -> bool { false }
 }
 
diff --git a/src/libgreen/simple.rs b/src/libgreen/simple.rs
index 6c33e7cc619fd..e26a099c02825 100644
--- a/src/libgreen/simple.rs
+++ b/src/libgreen/simple.rs
@@ -16,7 +16,6 @@ use std::mem;
 use std::rt::Runtime;
 use std::rt::local::Local;
 use std::rt::mutex::NativeMutex;
-use std::rt::rtio;
 use std::rt::task::{Task, BlockedTask, TaskOpts};
 
 struct SimpleTask {
@@ -79,9 +78,10 @@ impl Runtime for SimpleTask {
                      _f: proc():Send) {
         panic!()
     }
-    fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> { None }
+
     fn stack_bounds(&self) -> (uint, uint) { panic!() }
     fn stack_guard(&self) -> Option<uint> { panic!() }
+
     fn can_block(&self) -> bool { true }
     fn wrap(self: Box<SimpleTask>) -> Box<Any+'static> { panic!() }
 }
diff --git a/src/libgreen/task.rs b/src/libgreen/task.rs
index 0c549fa66c103..428b64144128b 100644
--- a/src/libgreen/task.rs
+++ b/src/libgreen/task.rs
@@ -24,7 +24,6 @@ use std::raw;
 use std::rt::Runtime;
 use std::rt::local::Local;
 use std::rt::mutex::NativeMutex;
-use std::rt::rtio;
 use std::rt::stack;
 use std::rt::task::{Task, BlockedTask, TaskOpts};
 use std::rt;
@@ -468,14 +467,6 @@ impl Runtime for GreenTask {
         sched.run_task(me, sibling)
     }
 
-    // Local I/O is provided by the scheduler's event loop
-    fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
-        match self.sched.as_mut().unwrap().event_loop.io() {
-            Some(io) => Some(rtio::LocalIo::new(io)),
-            None => None,
-        }
-    }
-
     fn stack_bounds(&self) -> (uint, uint) {
         let c = self.coroutine.as_ref()
             .expect("GreenTask.stack_bounds called without a coroutine");
diff --git a/src/libnative/io/mod.rs b/src/libnative/io/mod.rs
deleted file mode 100644
index 8c7751588cef3..0000000000000
--- a/src/libnative/io/mod.rs
+++ /dev/null
@@ -1,102 +0,0 @@
-// Copyright 2013-2014 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.
-
-//! Native thread-blocking I/O implementation
-//!
-//! This module contains the implementation of native thread-blocking
-//! implementations of I/O on all platforms. This module is not intended to be
-//! used directly, but rather the rust runtime will fall back to using it if
-//! necessary.
-//!
-//! Rust code normally runs inside of green tasks with a local scheduler using
-//! asynchronous I/O to cooperate among tasks. This model is not always
-//! available, however, and that's where these native implementations come into
-//! play. The only dependencies of these modules are the normal system libraries
-//! that you would find on the respective platform.
-
-#![allow(non_snake_case)]
-
-use libc::{mod, c_int};
-use std::c_str::CString;
-use std::os;
-use std::rt::rtio::{mod, IoResult, IoError};
-use std::num;
-
-#[cfg(windows)]
-#[path = "tty_windows.rs"]
-mod tty;
-
-fn unimpl() -> IoError {
-    #[cfg(unix)] use libc::ENOSYS as ERROR;
-    #[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;
-    IoError {
-        code: ERROR as uint,
-        extra: 0,
-        detail: Some("not yet supported by the `native` runtime, maybe try `green`.".to_string()),
-    }
-}
-
-fn last_error() -> IoError {
-    let errno = os::errno() as uint;
-    IoError {
-        code: os::errno() as uint,
-        extra: 0,
-        detail: Some(os::error_string(errno)),
-    }
-}
-
-#[cfg(windows)]
-#[inline]
-fn retry<I> (f: || -> I) -> I { f() } // PR rust-lang/rust/#17020
-
-#[cfg(unix)]
-#[inline]
-fn retry<I: PartialEq + num::One + Neg<I>> (f: || -> I) -> I {
-    let minus_one = -num::one::<I>();
-    loop {
-        let n = f();
-        if n == minus_one && os::errno() == libc::EINTR as int { }
-        else { return n }
-    }
-}
-
-
-fn keep_going(data: &[u8], f: |*const u8, uint| -> i64) -> i64 {
-    let origamt = data.len();
-    let mut data = data.as_ptr();
-    let mut amt = origamt;
-    while amt > 0 {
-        let ret = retry(|| f(data, amt));
-        if ret == 0 {
-            break
-        } else if ret != -1 {
-            amt -= ret as uint;
-            data = unsafe { data.offset(ret as int) };
-        } else {
-            return ret;
-        }
-    }
-    return (origamt - amt) as i64;
-}
-
-/// Implementation of rt::rtio's IoFactory trait to generate handles to the
-/// native I/O functionality.
-pub struct IoFactory {
-    _cannot_construct_outside_of_this_module: ()
-}
-
-impl IoFactory {
-    pub fn new() -> IoFactory {
-        IoFactory { _cannot_construct_outside_of_this_module: () }
-    }
-}
-
-impl rtio::IoFactory for IoFactory {
-}
diff --git a/src/libnative/lib.rs b/src/libnative/lib.rs
index c0ec4c16ab01f..4e25feb9d7531 100644
--- a/src/libnative/lib.rs
+++ b/src/libnative/lib.rs
@@ -74,7 +74,6 @@ use std::str;
 
 pub use task::NativeTaskBuilder;
 
-pub mod io;
 pub mod task;
 
 #[cfg(any(windows, android))]
diff --git a/src/libnative/task.rs b/src/libnative/task.rs
index e702c12bdffe3..6d640b61b18d3 100644
--- a/src/libnative/task.rs
+++ b/src/libnative/task.rs
@@ -19,13 +19,11 @@ use std::mem;
 use std::rt::bookkeeping;
 use std::rt::local::Local;
 use std::rt::mutex::NativeMutex;
-use std::rt::rtio;
 use std::rt::stack;
 use std::rt::task::{Task, BlockedTask, TaskOpts};
 use std::rt::thread::Thread;
 use std::rt;
 
-use io;
 use std::task::{TaskBuilder, Spawner};
 
 /// Creates a new Task which is ready to execute as a 1:1 task.
@@ -42,7 +40,6 @@ fn ops() -> Box<Ops> {
     box Ops {
         lock: unsafe { NativeMutex::new() },
         awoken: false,
-        io: io::IoFactory::new(),
         // these *should* get overwritten
         stack_bounds: (0, 0),
         stack_guard: 0
@@ -112,7 +109,6 @@ impl<S: Spawner> NativeTaskBuilder for TaskBuilder<S> {
 struct Ops {
     lock: NativeMutex,       // native synchronization
     awoken: bool,      // used to prevent spurious wakeups
-    io: io::IoFactory, // local I/O factory
 
     // This field holds the known bounds of the stack in (lo, hi) form. Not all
     // native tasks necessarily know their precise bounds, hence this is
@@ -272,10 +268,6 @@ impl rt::Runtime for Ops {
 
         NativeSpawner.spawn(opts, f);
     }
-
-    fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>> {
-        Some(rtio::LocalIo::new(&mut self.io as &mut rtio::IoFactory))
-    }
 }
 
 #[cfg(test)]
diff --git a/src/librustrt/lib.rs b/src/librustrt/lib.rs
index 9a8bd3cdfc81c..fee748e29d9e0 100644
--- a/src/librustrt/lib.rs
+++ b/src/librustrt/lib.rs
@@ -90,7 +90,6 @@ pub trait Runtime {
                      cur_task: Box<Task>,
                      opts: TaskOpts,
                      f: proc():Send);
-    fn local_io<'a>(&'a mut self) -> Option<rtio::LocalIo<'a>>;
     /// The (low, high) edges of the current stack.
     fn stack_bounds(&self) -> (uint, uint); // (lo, hi)
     /// The last writable byte of the stack next to the guard page
diff --git a/src/librustrt/rtio.rs b/src/librustrt/rtio.rs
index cdcefc2088e7c..86de8168189ca 100644
--- a/src/librustrt/rtio.rs
+++ b/src/librustrt/rtio.rs
@@ -12,12 +12,6 @@
 
 use core::prelude::*;
 use alloc::boxed::Box;
-use collections::string::String;
-use core::mem;
-use libc::c_int;
-
-use local::Local;
-use task::Task;
 
 pub trait EventLoop {
     fn run(&mut self);
@@ -27,8 +21,7 @@ pub trait EventLoop {
     fn remote_callback(&mut self, Box<Callback + Send>)
                        -> Box<RemoteCallback + Send>;
 
-    /// The asynchronous I/O services. Not all event loops may provide one.
-    fn io<'a>(&'a mut self) -> Option<&'a mut IoFactory>;
+    // last vestige of IoFactory
     fn has_active_io(&self) -> bool;
 }
 
@@ -46,128 +39,7 @@ pub trait RemoteCallback {
     fn fire(&mut self);
 }
 
-pub struct LocalIo<'a> {
-    factory: &'a mut IoFactory+'a,
-}
-
-#[unsafe_destructor]
-impl<'a> Drop for LocalIo<'a> {
-    fn drop(&mut self) {
-        // FIXME(pcwalton): Do nothing here for now, but eventually we may want
-        // something. For now this serves to make `LocalIo` noncopyable.
-    }
-}
-
-impl<'a> LocalIo<'a> {
-    /// Returns the local I/O: either the local scheduler's I/O services or
-    /// the native I/O services.
-    pub fn borrow() -> Option<LocalIo<'a>> {
-        // FIXME(#11053): bad
-        //
-        // This is currently very unsafely implemented. We don't actually
-        // *take* the local I/O so there's a very real possibility that we
-        // can have two borrows at once. Currently there is not a clear way
-        // to actually borrow the local I/O factory safely because even if
-        // ownership were transferred down to the functions that the I/O
-        // factory implements it's just too much of a pain to know when to
-        // relinquish ownership back into the local task (but that would be
-        // the safe way of implementing this function).
-        //
-        // In order to get around this, we just transmute a copy out of the task
-        // in order to have what is likely a static lifetime (bad).
-        let mut t: Box<Task> = match Local::try_take() {
-            Some(t) => t,
-            None => return None,
-        };
-        let ret = t.local_io().map(|t| {
-            unsafe { mem::transmute_copy(&t) }
-        });
-        Local::put(t);
-        return ret;
-    }
-
-    pub fn maybe_raise<T>(f: |io: &mut IoFactory| -> IoResult<T>)
-        -> IoResult<T>
-    {
-        #[cfg(unix)] use libc::EINVAL as ERROR;
-        #[cfg(windows)] use libc::ERROR_CALL_NOT_IMPLEMENTED as ERROR;
-        match LocalIo::borrow() {
-            Some(mut io) => f(io.get()),
-            None => Err(IoError {
-                code: ERROR as uint,
-                extra: 0,
-                detail: None,
-            }),
-        }
-    }
-
-    pub fn new<'a>(io: &'a mut IoFactory+'a) -> LocalIo<'a> {
-        LocalIo { factory: io }
-    }
-
-    /// Returns the underlying I/O factory as a trait reference.
-    #[inline]
-    pub fn get<'a>(&'a mut self) -> &'a mut IoFactory {
-        let f: &'a mut IoFactory = self.factory;
-        f
-    }
-}
-
-pub trait IoFactory {
-    fn timer_init(&mut self) -> IoResult<Box<RtioTimer + Send>>;
-    fn tty_open(&mut self, fd: c_int, readable: bool)
-            -> IoResult<Box<RtioTTY + Send>>;
-}
-
-pub trait RtioTimer {
-    fn sleep(&mut self, msecs: u64);
-    fn oneshot(&mut self, msecs: u64, cb: Box<Callback + Send>);
-    fn period(&mut self, msecs: u64, cb: Box<Callback + Send>);
-}
-
-pub trait RtioPipe {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
-    fn write(&mut self, buf: &[u8]) -> IoResult<()>;
-    fn clone(&self) -> Box<RtioPipe + Send>;
-
-    fn close_write(&mut self) -> IoResult<()>;
-    fn close_read(&mut self) -> IoResult<()>;
-    fn set_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_read_timeout(&mut self, timeout_ms: Option<u64>);
-    fn set_write_timeout(&mut self, timeout_ms: Option<u64>);
-}
-
-pub trait RtioUnixListener {
-    fn listen(self: Box<Self>) -> IoResult<Box<RtioUnixAcceptor + Send>>;
-}
-
-pub trait RtioUnixAcceptor {
-    fn accept(&mut self) -> IoResult<Box<RtioPipe + Send>>;
-    fn set_timeout(&mut self, timeout: Option<u64>);
-    fn clone(&self) -> Box<RtioUnixAcceptor + Send>;
-    fn close_accept(&mut self) -> IoResult<()>;
-}
-
-pub trait RtioTTY {
-    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint>;
-    fn write(&mut self, buf: &[u8]) -> IoResult<()>;
-    fn set_raw(&mut self, raw: bool) -> IoResult<()>;
-    fn get_winsize(&mut self) -> IoResult<(int, int)>;
-    fn isatty(&self) -> bool;
-}
-
 pub trait PausableIdleCallback {
     fn pause(&mut self);
     fn resume(&mut self);
 }
-
-pub trait RtioSignal {}
-
-#[deriving(Show)]
-pub struct IoError {
-    pub code: uint,
-    pub extra: uint,
-    pub detail: Option<String>,
-}
-
-pub type IoResult<T> = Result<T, IoError>;
diff --git a/src/librustrt/task.rs b/src/librustrt/task.rs
index ad3b1dc7e1695..554e4784eac51 100644
--- a/src/librustrt/task.rs
+++ b/src/librustrt/task.rs
@@ -26,7 +26,6 @@ use core::raw;
 use local_data;
 use Runtime;
 use local::Local;
-use rtio::LocalIo;
 use unwind;
 use unwind::Unwinder;
 use collections::str::SendStr;
@@ -421,13 +420,6 @@ impl Task {
         ops.maybe_yield(self);
     }
 
-    /// Acquires a handle to the I/O factory that this task contains, normally
-    /// stored in the task's runtime. This factory may not always be available,
-    /// which is why the return type is `Option`
-    pub fn local_io<'a>(&'a mut self) -> Option<LocalIo<'a>> {
-        self.imp.as_mut().unwrap().local_io()
-    }
-
     /// Returns the stack bounds for this task in (lo, hi) format. The stack
     /// bounds may not be known for all tasks, so the return value may be
     /// `None`.
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 03c073c1477d5..31eab4363d0aa 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -228,15 +228,15 @@ use error::{FromError, Error};
 use fmt;
 use int;
 use iter::Iterator;
-use libc;
 use mem::transmute;
 use ops::{BitOr, BitXor, BitAnd, Sub, Not};
 use option::{Option, Some, None};
 use os;
 use boxed::Box;
 use result::{Ok, Err, Result};
-use rt::rtio;
 use sys;
+use slice::{AsSlice, SlicePrelude};
+use str::{Str, StrPrelude};
 use str;
 use string::String;
 use uint;
@@ -328,17 +328,6 @@ impl IoError {
     pub fn last_error() -> IoError {
         IoError::from_errno(os::errno() as uint, true)
     }
-
-    fn from_rtio_error(err: rtio::IoError) -> IoError {
-        let rtio::IoError { code, extra, detail } = err;
-        let mut ioerr = IoError::from_errno(code, false);
-        ioerr.detail = detail;
-        ioerr.kind = match ioerr.kind {
-            TimedOut if extra > 0 => ShortWrite(extra),
-            k => k,
-        };
-        return ioerr;
-    }
 }
 
 impl fmt::Show for IoError {
diff --git a/src/libstd/sys/unix/mod.rs b/src/libstd/sys/unix/mod.rs
index 4bd1dd2016328..d3f55d59534d3 100644
--- a/src/libstd/sys/unix/mod.rs
+++ b/src/libstd/sys/unix/mod.rs
@@ -9,6 +9,11 @@
 // except according to those terms.
 
 #![allow(missing_doc)]
+#![allow(non_camel_case_types)]
+#![allow(unused_imports)]
+#![allow(dead_code)]
+#![allow(unused_unsafe)]
+#![allow(unused_mut)]
 
 extern crate libc;
 

From 0bea593ea2fb885020ffc73ed00531d4debae9d1 Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Sat, 8 Nov 2014 17:22:14 -0800
Subject: [PATCH 10/11] Remove somewhat bogus process-spawn-errno test
 (non-mac, non-windows only)

---
 src/test/run-pass/unix-process-spawn-errno.rs | 95 -------------------
 1 file changed, 95 deletions(-)
 delete mode 100644 src/test/run-pass/unix-process-spawn-errno.rs

diff --git a/src/test/run-pass/unix-process-spawn-errno.rs b/src/test/run-pass/unix-process-spawn-errno.rs
deleted file mode 100644
index b2ef1a044db80..0000000000000
--- a/src/test/run-pass/unix-process-spawn-errno.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2014 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.
-
-// ignore-windows
-// ignore-macos
-
-#![feature(macro_rules)]
-
-extern crate native;
-extern crate rustrt;
-extern crate libc;
-use libc::{c_char, c_int};
-use native::io::process;
-use rustrt::rtio;
-use rustrt::c_str;
-
-macro_rules! c_string {
-    ($s:expr) => { {
-        let ptr = concat!($s, "\0").as_ptr() as *const i8;
-        unsafe { &c_str::CString::new(ptr, false) }
-    } }
-}
-
-static EXPECTED_ERRNO: c_int = 0x778899aa;
-
-#[no_mangle]
-pub unsafe extern "C" fn chdir(_: *const c_char) -> c_int {
-    // copied from std::os::errno()
-    #[cfg(any(target_os = "macos",
-              target_os = "ios",
-              target_os = "freebsd"))]
-    fn errno_location() -> *mut c_int {
-        extern {
-            fn __error() -> *mut c_int;
-        }
-        unsafe {
-            __error()
-        }
-    }
-
-    #[cfg(target_os = "dragonfly")]
-    fn errno_location() -> *mut c_int {
-        extern {
-            fn __dfly_error() -> *mut c_int;
-        }
-        unsafe {
-            __dfly_error()
-        }
-    }
-
-    #[cfg(any(target_os = "linux", target_os = "android"))]
-    fn errno_location() -> *mut c_int {
-        extern {
-            fn __errno_location() -> *mut c_int;
-        }
-        unsafe {
-            __errno_location()
-        }
-    }
-
-    *errno_location() = EXPECTED_ERRNO;
-    return -1;
-}
-
-fn main() {
-    let program = c_string!("true");
-    let cwd = c_string!("whatever");
-    let cfg = rtio::ProcessConfig {
-        program: program,
-        args: &[],
-        env: None,
-        cwd: Some(cwd),
-        stdin: rtio::Ignored,
-        stdout: rtio::Ignored,
-        stderr: rtio::Ignored,
-        extra_io: &[],
-        uid: None,
-        gid: None,
-        detach: false
-    };
-
-    match process::Process::spawn(cfg) {
-        Ok(_) => { panic!("spawn() should have panicked"); }
-        Err(rtio::IoError { code: err, ..}) => {
-            assert_eq!(err as c_int, EXPECTED_ERRNO);
-        }
-    };
-}

From 5ea09e6a25816fb6f0aca5adb874c623981653df Mon Sep 17 00:00:00 2001
From: Aaron Turon <aturon@mozilla.com>
Date: Sat, 8 Nov 2014 20:57:15 -0800
Subject: [PATCH 11/11] Ignore sepcomp-lib-lto on android due to linker
 weirdness

---
 src/test/run-pass/sepcomp-lib-lto.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/test/run-pass/sepcomp-lib-lto.rs b/src/test/run-pass/sepcomp-lib-lto.rs
index f0b6a505929c6..002671ff517a1 100644
--- a/src/test/run-pass/sepcomp-lib-lto.rs
+++ b/src/test/run-pass/sepcomp-lib-lto.rs
@@ -11,6 +11,7 @@
 // Check that we can use `-C lto` when linking against libraries that were
 // separately compiled.
 
+// ignore-android linker weridness (see #18800)
 // aux-build:sepcomp_lib.rs
 // compile-flags: -C lto
 // no-prefer-dynamic