about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/io/mod.rs19
-rw-r--r--src/libstd/io/native/file.rs232
-rw-r--r--src/libstd/io/native/mod.rs200
-rw-r--r--src/libstd/io/native/process.rs186
-rw-r--r--src/libstd/io/native/stdio.rs63
-rw-r--r--src/libstd/io/stdio.rs24
-rw-r--r--src/libstd/rt/basic.rs10
-rw-r--r--src/libstd/rt/rtio.rs26
-rw-r--r--src/libstd/rt/util.rs24
9 files changed, 434 insertions, 350 deletions
diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs
index 8858d0a14ad..2aed3bdb849 100644
--- a/src/libstd/io/mod.rs
+++ b/src/libstd/io/mod.rs
@@ -306,24 +306,7 @@ pub mod timer;
 /// Buffered I/O wrappers
 pub mod buffered;
 
-/// Thread-blocking implementations
-pub mod native {
-    /// Posix file I/O
-    pub mod file;
-    /// Process spawning and child management
-    pub mod process;
-    /// Posix stdio
-    pub mod stdio;
-
-    /// Sockets
-    /// # XXX - implement this
-    pub mod net {
-        pub mod tcp { }
-        pub mod udp { }
-        #[cfg(unix)]
-        pub mod unix { }
-    }
-}
+pub mod native;
 
 /// Signal handling
 pub mod signal;
diff --git a/src/libstd/io/native/file.rs b/src/libstd/io/native/file.rs
index 0f1a64edb60..5e39460ba6a 100644
--- a/src/libstd/io/native/file.rs
+++ b/src/libstd/io/native/file.rs
@@ -12,42 +12,16 @@
 
 #[allow(non_camel_case_types)];
 
+use io::IoError;
+use io;
 use libc;
+use ops::Drop;
+use option::{Some, None, Option};
 use os;
-use prelude::*;
-use super::super::*;
-
-#[cfg(windows)]
-fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
-    match errno {
-        libc::EOF => (EndOfFile, "end of file"),
-        _ => (OtherIoError, "unknown error"),
-    }
-}
-
-#[cfg(not(windows))]
-fn get_err(errno: i32) -> (IoErrorKind, &'static str) {
-    // XXX: this should probably be a bit more descriptive...
-    match errno {
-        libc::EOF => (EndOfFile, "end of file"),
-
-        // 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"),
-    }
-}
-
-fn raise_error() {
-    let (kind, desc) = get_err(os::errno() as i32);
-    io_error::cond.raise(IoError {
-        kind: kind,
-        desc: desc,
-        detail: Some(os::last_os_error())
-    });
-}
+use ptr::RawPtr;
+use result::{Result, Ok, Err};
+use rt::rtio;
+use vec::ImmutableVector;
 
 fn keep_going(data: &[u8], f: &fn(*u8, uint) -> i64) -> i64 {
     #[cfg(windows)] static eintr: int = 0; // doesn't matter
@@ -95,10 +69,8 @@ impl FileDesc {
     pub fn new(fd: fd_t, close_on_drop: bool) -> FileDesc {
         FileDesc { fd: fd, close_on_drop: close_on_drop }
     }
-}
 
-impl Reader for FileDesc {
-    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+    fn inner_read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
         #[cfg(windows)] type rlen = libc::c_uint;
         #[cfg(not(windows))] type rlen = libc::size_t;
         let ret = do keep_going(buf) |buf, len| {
@@ -107,20 +79,14 @@ impl Reader for FileDesc {
             }
         };
         if ret == 0 {
-            None
+            Err(io::standard_error(io::EndOfFile))
         } else if ret < 0 {
-            raise_error();
-            None
+            Err(super::last_error())
         } else {
-            Some(ret as uint)
+            Ok(ret as uint)
         }
     }
-
-    fn eof(&mut self) -> bool { false }
-}
-
-impl Writer for FileDesc {
-    fn write(&mut self, buf: &[u8]) {
+    fn inner_write(&mut self, buf: &[u8]) -> Result<(), IoError> {
         #[cfg(windows)] type wlen = libc::c_uint;
         #[cfg(not(windows))] type wlen = libc::size_t;
         let ret = do keep_going(buf) |buf, len| {
@@ -129,14 +95,84 @@ impl Writer for FileDesc {
             }
         };
         if ret < 0 {
-            raise_error();
+            Err(super::last_error())
+        } else {
+            Ok(())
         }
     }
 }
 
+impl io::Reader for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+        match self.inner_read(buf) { Ok(n) => Some(n), Err(*) => None }
+    }
+    fn eof(&mut self) -> bool { false }
+}
+
+impl io::Writer for FileDesc {
+    fn write(&mut self, buf: &[u8]) {
+        self.inner_write(buf);
+    }
+}
+
+impl rtio::RtioFileStream for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
+        self.inner_read(buf).map(|i| i as int)
+    }
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
+        self.inner_write(buf)
+    }
+    fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result<int, IoError> {
+        Err(super::unimpl())
+    }
+    fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn seek(&mut self, _pos: i64, _whence: io::SeekStyle) -> Result<u64, IoError> {
+        Err(super::unimpl())
+    }
+    fn tell(&self) -> Result<u64, IoError> {
+        Err(super::unimpl())
+    }
+    fn fsync(&mut self) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn datasync(&mut self) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn truncate(&mut self, _offset: i64) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+}
+
+impl rtio::RtioPipe for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
+        self.inner_read(buf)
+    }
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
+        self.inner_write(buf)
+    }
+}
+
+impl rtio::RtioTTY for FileDesc {
+    fn read(&mut self, buf: &mut [u8]) -> Result<uint, IoError> {
+        self.inner_read(buf)
+    }
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
+        self.inner_write(buf)
+    }
+    fn set_raw(&mut self, _raw: bool) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn get_winsize(&mut self) -> Result<(int, int), IoError> {
+        Err(super::unimpl())
+    }
+}
+
 impl Drop for FileDesc {
     fn drop(&mut self) {
-        if self.close_on_drop {
+        // closing stdio file handles makes no sense, so never do it
+        if self.close_on_drop && self.fd > libc::STDERR_FILENO {
             unsafe { libc::close(self.fd); }
         }
     }
@@ -154,8 +190,8 @@ impl CFile {
     pub fn new(file: *libc::FILE) -> CFile { CFile { file: file } }
 }
 
-impl Reader for CFile {
-    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
+impl rtio::RtioFileStream for CFile {
+    fn read(&mut self, buf: &mut [u8]) -> Result<int, IoError> {
         let ret = do keep_going(buf) |buf, len| {
             unsafe {
                 libc::fread(buf as *mut libc::c_void, 1, len as libc::size_t,
@@ -163,22 +199,15 @@ impl Reader for CFile {
             }
         };
         if ret == 0 {
-            None
+            Err(io::standard_error(io::EndOfFile))
         } else if ret < 0 {
-            raise_error();
-            None
+            Err(super::last_error())
         } else {
-            Some(ret as uint)
+            Ok(ret as int)
         }
     }
 
-    fn eof(&mut self) -> bool {
-        unsafe { libc::feof(self.file) != 0 }
-    }
-}
-
-impl Writer for CFile {
-    fn write(&mut self, buf: &[u8]) {
+    fn write(&mut self, buf: &[u8]) -> Result<(), IoError> {
         let ret = do keep_going(buf) |buf, len| {
             unsafe {
                 libc::fwrite(buf as *libc::c_void, 1, len as libc::size_t,
@@ -186,35 +215,47 @@ impl Writer for CFile {
             }
         };
         if ret < 0 {
-            raise_error();
+            Err(super::last_error())
+        } else {
+            Ok(())
         }
     }
 
-    fn flush(&mut self) {
-        if unsafe { libc::fflush(self.file) } < 0 {
-            raise_error();
+    fn pread(&mut self, _buf: &mut [u8], _offset: u64) -> Result<int, IoError> {
+        Err(super::unimpl())
+    }
+    fn pwrite(&mut self, _buf: &[u8], _offset: u64) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn seek(&mut self, pos: i64, style: io::SeekStyle) -> Result<u64, IoError> {
+        let whence = match style {
+            io::SeekSet => libc::SEEK_SET,
+            io::SeekEnd => libc::SEEK_END,
+            io::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)
         }
     }
-}
-
-impl Seek for CFile {
-    fn tell(&self) -> u64 {
+    fn tell(&self) -> Result<u64, IoError> {
         let ret = unsafe { libc::ftell(self.file) };
         if ret < 0 {
-            raise_error();
+            Err(super::last_error())
+        } else {
+            Ok(ret as u64)
         }
-        return ret as u64;
     }
-
-    fn seek(&mut self, pos: i64, style: SeekStyle) {
-        let whence = match style {
-            SeekSet => libc::SEEK_SET,
-            SeekEnd => libc::SEEK_END,
-            SeekCur => libc::SEEK_CUR,
-        };
-        if unsafe { libc::fseek(self.file, pos as libc::c_long, whence) } < 0 {
-            raise_error();
-        }
+    fn fsync(&mut self) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn datasync(&mut self) -> Result<(), IoError> {
+        Err(super::unimpl())
+    }
+    fn truncate(&mut self, _offset: i64) -> Result<(), IoError> {
+        Err(super::unimpl())
     }
 }
 
@@ -228,9 +269,9 @@ impl Drop for CFile {
 mod tests {
     use libc;
     use os;
-    use prelude::*;
-    use io::{io_error, SeekSet};
-    use super::*;
+    use io::{io_error, SeekSet, Writer, Reader};
+    use result::Ok;
+    use super::{CFile, FileDesc};
 
     #[ignore(cfg(target_os = "freebsd"))] // hmm, maybe pipes have a tiny buffer
     fn test_file_desc() {
@@ -241,10 +282,10 @@ mod tests {
             let mut reader = FileDesc::new(input, true);
             let mut writer = FileDesc::new(out, true);
 
-            writer.write(bytes!("test"));
+            writer.inner_write(bytes!("test"));
             let mut buf = [0u8, ..4];
-            match reader.read(buf) {
-                Some(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);
@@ -253,17 +294,8 @@ mod tests {
                 r => fail!("invalid read: {:?}", r)
             }
 
-            let mut raised = false;
-            do io_error::cond.trap(|_| { raised = true; }).inside {
-                writer.read(buf);
-            }
-            assert!(raised);
-
-            raised = false;
-            do io_error::cond.trap(|_| { raised = true; }).inside {
-                reader.write(buf);
-            }
-            assert!(raised);
+            assert!(writer.inner_read(buf).is_err());
+            assert!(reader.inner_write(buf).is_err());
         }
     }
 
@@ -278,7 +310,7 @@ mod tests {
             let mut buf = [0u8, ..4];
             file.seek(0, SeekSet);
             match file.read(buf) {
-                Some(4) => {
+                Ok(4) => {
                     assert_eq!(buf[0], 't' as u8);
                     assert_eq!(buf[1], 'e' as u8);
                     assert_eq!(buf[2], 's' as u8);
diff --git a/src/libstd/io/native/mod.rs b/src/libstd/io/native/mod.rs
new file mode 100644
index 00000000000..d7aa6c14fcd
--- /dev/null
+++ b/src/libstd/io/native/mod.rs
@@ -0,0 +1,200 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! 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.
+
+use c_str::CString;
+use comm::SharedChan;
+use libc::c_int;
+use libc;
+use option::{Option, None, Some};
+use os;
+use path::Path;
+use result::{Result, Ok, Err};
+use rt::rtio;
+use rt::rtio::{RtioTcpStream, RtioTcpListener, RtioUdpSocket, RtioUnixListener,
+               RtioPipe, RtioFileStream, RtioProcess, RtioSignal, RtioTTY,
+               CloseBehavior, RtioTimer};
+use io;
+use io::IoError;
+use io::net::ip::SocketAddr;
+use io::process::ProcessConfig;
+use io::signal::Signum;
+use ai = io::net::addrinfo;
+
+// Local re-exports
+pub use self::file::FileDesc;
+pub use self::process::Process;
+
+// Native I/O implementations
+pub mod file;
+pub mod process;
+
+type IoResult<T> = Result<T, IoError>;
+
+fn unimpl() -> IoError {
+    IoError {
+        kind: io::IoUnavailable,
+        desc: "unimplemented I/O interface",
+        detail: None,
+    }
+}
+
+fn last_error() -> IoError {
+    #[cfg(windows)]
+    fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
+        match errno {
+            libc::EOF => (io::EndOfFile, "end of file"),
+            _ => (io::OtherIoError, "unknown error"),
+        }
+    }
+
+    #[cfg(not(windows))]
+    fn get_err(errno: i32) -> (io::IoErrorKind, &'static str) {
+        // XXX: this should probably be a bit more descriptive...
+        match errno {
+            libc::EOF => (io::EndOfFile, "end of file"),
+
+            // 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"),
+        }
+    }
+
+    let (kind, desc) = get_err(os::errno() as i32);
+    IoError {
+        kind: kind,
+        desc: desc,
+        detail: Some(os::last_os_error())
+    }
+}
+
+/// Implementation of rt::rtio's IoFactory trait to generate handles to the
+/// native I/O functionality.
+pub struct IoFactory;
+
+impl rtio::IoFactory for IoFactory {
+    // networking
+    fn tcp_connect(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpStream> {
+        Err(unimpl())
+    }
+    fn tcp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioTcpListener> {
+        Err(unimpl())
+    }
+    fn udp_bind(&mut self, _addr: SocketAddr) -> IoResult<~RtioUdpSocket> {
+        Err(unimpl())
+    }
+    fn unix_bind(&mut self, _path: &CString) -> IoResult<~RtioUnixListener> {
+        Err(unimpl())
+    }
+    fn unix_connect(&mut self, _path: &CString) -> IoResult<~RtioPipe> {
+        Err(unimpl())
+    }
+    fn get_host_addresses(&mut self, _host: Option<&str>, _servname: Option<&str>,
+                          _hint: Option<ai::Hint>) -> IoResult<~[ai::Info]> {
+        Err(unimpl())
+    }
+
+    // filesystem operations
+    fn fs_from_raw_fd(&mut self, fd: c_int,
+                      close: CloseBehavior) -> ~RtioFileStream {
+        let close = match close {
+            rtio::CloseSynchronously | rtio::CloseAsynchronously => true,
+            rtio::DontClose => false
+        };
+        ~file::FileDesc::new(fd, close) as ~RtioFileStream
+    }
+    fn fs_open(&mut self, _path: &CString, _fm: io::FileMode, _fa: io::FileAccess)
+        -> IoResult<~RtioFileStream> {
+        Err(unimpl())
+    }
+    fn fs_unlink(&mut self, _path: &CString) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_stat(&mut self, _path: &CString) -> IoResult<io::FileStat> {
+        Err(unimpl())
+    }
+    fn fs_mkdir(&mut self, _path: &CString,
+                _mode: io::FilePermission) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_chmod(&mut self, _path: &CString,
+                _mode: io::FilePermission) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_rmdir(&mut self, _path: &CString) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_rename(&mut self, _path: &CString, _to: &CString) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_readdir(&mut self, _path: &CString, _flags: c_int) -> IoResult<~[Path]> {
+        Err(unimpl())
+    }
+    fn fs_lstat(&mut self, _path: &CString) -> IoResult<io::FileStat> {
+        Err(unimpl())
+    }
+    fn fs_chown(&mut self, _path: &CString, _uid: int, _gid: int) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_readlink(&mut self, _path: &CString) -> IoResult<Path> {
+        Err(unimpl())
+    }
+    fn fs_symlink(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_link(&mut self, _src: &CString, _dst: &CString) -> IoResult<()> {
+        Err(unimpl())
+    }
+    fn fs_utime(&mut self, _src: &CString, _atime: u64,
+                _mtime: u64) -> IoResult<()> {
+        Err(unimpl())
+    }
+
+    // misc
+    fn timer_init(&mut self) -> IoResult<~RtioTimer> {
+        Err(unimpl())
+    }
+    fn spawn(&mut self, config: ProcessConfig)
+            -> IoResult<(~RtioProcess, ~[Option<~RtioPipe>])> {
+        process::Process::spawn(config).map(|(p, io)| {
+            (~p as ~RtioProcess,
+             io.move_iter().map(|p| p.map(|p| ~p as ~RtioPipe)).collect())
+        })
+    }
+    fn pipe_open(&mut self, _fd: c_int) -> IoResult<~RtioPipe> {
+        Err(unimpl())
+    }
+    fn tty_open(&mut self, fd: c_int, _readable: bool) -> IoResult<~RtioTTY> {
+        if unsafe { libc::isatty(fd) } != 0 {
+            Ok(~file::FileDesc::new(fd, true) as ~RtioTTY)
+        } else {
+            Err(unimpl())
+        }
+    }
+    fn signal(&mut self, _signal: Signum, _channel: SharedChan<Signum>)
+        -> IoResult<~RtioSignal> {
+        Err(unimpl())
+    }
+}
diff --git a/src/libstd/io/native/process.rs b/src/libstd/io/native/process.rs
index de03ac1c07d..71c6ce78a1e 100644
--- a/src/libstd/io/native/process.rs
+++ b/src/libstd/io/native/process.rs
@@ -9,14 +9,17 @@
 // except according to those terms.
 
 use cast;
+use io;
 use libc::{pid_t, c_void, c_int};
 use libc;
 use os;
 use prelude::*;
 use ptr;
-use io;
+use rt::rtio;
 use super::file;
 
+use p = io::process;
+
 /**
  * A value representing a child process.
  *
@@ -33,13 +36,6 @@ pub struct Process {
     /// pid being re-used until the handle is closed.
     priv handle: *(),
 
-    /// Currently known stdin of the child, if any
-    priv input: Option<file::FileDesc>,
-    /// Currently known stdout of the child, if any
-    priv output: Option<file::FileDesc>,
-    /// Currently known stderr of the child, if any
-    priv error: Option<file::FileDesc>,
-
     /// None until finish() is called.
     priv exit_code: Option<int>,
 }
@@ -64,35 +60,44 @@ impl Process {
     ///     these are `None`, then this module will bind the input/output to an
     ///     os pipe instead. This process takes ownership of these file
     ///     descriptors, closing them upon destruction of the process.
-    pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>,
-               cwd: Option<&Path>,
-               stdin: Option<file::fd_t>,
-               stdout: Option<file::fd_t>,
-               stderr: Option<file::fd_t>) -> Process {
-        let (in_pipe, in_fd) = match stdin {
-            None => {
-                let pipe = os::pipe();
-                (Some(pipe), pipe.input)
-            },
-            Some(fd) => (None, fd)
-        };
-        let (out_pipe, out_fd) = match stdout {
-            None => {
-                let pipe = os::pipe();
-                (Some(pipe), pipe.out)
-            },
-            Some(fd) => (None, fd)
-        };
-        let (err_pipe, err_fd) = match stderr {
-            None => {
-                let pipe = os::pipe();
-                (Some(pipe), pipe.out)
-            },
-            Some(fd) => (None, fd)
-        };
+    pub fn spawn(config: p::ProcessConfig)
+        -> Result<(Process, ~[Option<file::FileDesc>]), io::IoError>
+    {
+        // right now we only handle stdin/stdout/stderr.
+        if config.io.len() > 3 {
+            return Err(super::unimpl());
+        }
+
+        fn get_io(io: &[p::StdioContainer],
+                  ret: &mut ~[Option<file::FileDesc>],
+                  idx: uint) -> (Option<os::Pipe>, c_int) {
+            if idx >= io.len() { return (None, -1); }
+            ret.push(None);
+            match io[idx] {
+                p::Ignored => (None, -1),
+                p::InheritFd(fd) => (None, fd),
+                p::CreatePipe(readable, _writable) => {
+                    let pipe = os::pipe();
+                    let (theirs, ours) = if readable {
+                        (pipe.input, pipe.out)
+                    } else {
+                        (pipe.out, pipe.input)
+                    };
+                    ret[idx] = Some(file::FileDesc::new(ours, true));
+                    (Some(pipe), theirs)
+                }
+            }
+        }
+
+        let mut ret_io = ~[];
+        let (in_pipe, in_fd) = get_io(config.io, &mut ret_io, 0);
+        let (out_pipe, out_fd) = get_io(config.io, &mut ret_io, 1);
+        let (err_pipe, err_fd) = get_io(config.io, &mut ret_io, 2);
 
-        let res = spawn_process_os(prog, args, env, cwd,
-                                   in_fd, out_fd, err_fd);
+        let env = config.env.map(|a| a.to_owned());
+        let cwd = config.cwd.map(|a| Path::new(a));
+        let res = spawn_process_os(config.program, config.args, env,
+                                   cwd.as_ref(), in_fd, out_fd, err_fd);
 
         unsafe {
             for pipe in in_pipe.iter() { libc::close(pipe.input); }
@@ -100,97 +105,26 @@ impl Process {
             for pipe in err_pipe.iter() { libc::close(pipe.out); }
         }
 
-        Process {
-            pid: res.pid,
-            handle: res.handle,
-            input: in_pipe.map(|pipe| file::FileDesc::new(pipe.out, true)),
-            output: out_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)),
-            error: err_pipe.map(|pipe| file::FileDesc::new(pipe.input, true)),
-            exit_code: None,
-        }
-    }
-
-    /// Returns the unique id of the process
-    pub fn id(&self) -> pid_t { self.pid }
-
-    /**
-     * Returns an io::Writer that can be used to write to this Process's stdin.
-     *
-     * Fails if there is no stdinavailable (it's already been removed by
-     * take_input)
-     */
-    pub fn input<'a>(&'a mut self) -> &'a mut io::Writer {
-        match self.input {
-            Some(ref mut fd) => fd as &mut io::Writer,
-            None => fail!("This process has no stdin")
-        }
-    }
-
-    /**
-     * Returns an io::Reader that can be used to read from this Process's
-     * stdout.
-     *
-     * Fails if there is no stdin available (it's already been removed by
-     * take_output)
-     */
-    pub fn output<'a>(&'a mut self) -> &'a mut io::Reader {
-        match self.input {
-            Some(ref mut fd) => fd as &mut io::Reader,
-            None => fail!("This process has no stdout")
-        }
-    }
-
-    /**
-     * Returns an io::Reader that can be used to read from this Process's
-     * stderr.
-     *
-     * Fails if there is no stdin available (it's already been removed by
-     * take_error)
-     */
-    pub fn error<'a>(&'a mut self) -> &'a mut io::Reader {
-        match self.error {
-            Some(ref mut fd) => fd as &mut io::Reader,
-            None => fail!("This process has no stderr")
-        }
-    }
-
-    /**
-     * Takes the stdin of this process, transferring ownership to the caller.
-     * Note that when the return value is destroyed, the handle will be closed
-     * for the child process.
-     */
-    pub fn take_input(&mut self) -> Option<~io::Writer> {
-        self.input.take().map(|fd| ~fd as ~io::Writer)
-    }
-
-    /**
-     * Takes the stdout of this process, transferring ownership to the caller.
-     * Note that when the return value is destroyed, the handle will be closed
-     * for the child process.
-     */
-    pub fn take_output(&mut self) -> Option<~io::Reader> {
-        self.output.take().map(|fd| ~fd as ~io::Reader)
+        Ok((Process { pid: res.pid, handle: res.handle, exit_code: None }, ret_io))
     }
+}
 
-    /**
-     * Takes the stderr of this process, transferring ownership to the caller.
-     * Note that when the return value is destroyed, the handle will be closed
-     * for the child process.
-     */
-    pub fn take_error(&mut self) -> Option<~io::Reader> {
-        self.error.take().map(|fd| ~fd as ~io::Reader)
-    }
+impl rtio::RtioProcess for Process {
+    fn id(&self) -> pid_t { self.pid }
 
-    pub fn wait(&mut self) -> int {
-        for &code in self.exit_code.iter() {
-            return code;
-        }
-        let code = waitpid(self.pid);
-        self.exit_code = Some(code);
-        return code;
+    fn wait(&mut self) -> p::ProcessExit {
+        let code = match self.exit_code {
+            Some(code) => code,
+            None => {
+                let code = waitpid(self.pid);
+                self.exit_code = Some(code);
+                code
+            }
+        };
+        return p::ExitStatus(code); // XXX: this is wrong
     }
 
-    pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> {
+    fn kill(&mut self, signum: int) -> Result<(), io::IoError> {
         // 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
@@ -207,8 +141,7 @@ impl Process {
         #[cfg(windows)]
         unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
             match signal {
-                io::process::PleaseExitSignal |
-                io::process::MustDieSignal => {
+                io::process::PleaseExitSignal | io::process::MustDieSignal => {
                     libc::funcs::extra::kernel32::TerminateProcess(
                         cast::transmute(pid), 1);
                     Ok(())
@@ -231,11 +164,6 @@ impl Process {
 
 impl Drop for Process {
     fn drop(&mut self) {
-        // close all these handles
-        self.take_input();
-        self.take_output();
-        self.take_error();
-        self.wait();
         free_handle(self.handle);
     }
 }
diff --git a/src/libstd/io/native/stdio.rs b/src/libstd/io/native/stdio.rs
deleted file mode 100644
index 68748ab49a3..00000000000
--- a/src/libstd/io/native/stdio.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use libc;
-use option::Option;
-use io::{Reader, Writer};
-use super::file;
-
-/// Creates a new handle to the stdin of this process
-pub fn stdin() -> StdIn { StdIn::new() }
-/// Creates a new handle to the stdout of this process
-pub fn stdout() -> StdOut { StdOut::new(libc::STDOUT_FILENO) }
-/// Creates a new handle to the stderr of this process
-pub fn stderr() -> StdOut { StdOut::new(libc::STDERR_FILENO) }
-
-pub fn print(s: &str) {
-    stdout().write(s.as_bytes())
-}
-
-pub fn println(s: &str) {
-    let mut out = stdout();
-    out.write(s.as_bytes());
-    out.write(['\n' as u8]);
-}
-
-pub struct StdIn {
-    priv fd: file::FileDesc
-}
-
-impl StdIn {
-    /// Duplicates the stdin file descriptor, returning an io::Reader
-    pub fn new() -> StdIn {
-        StdIn { fd: file::FileDesc::new(libc::STDIN_FILENO, false) }
-    }
-}
-
-impl Reader for StdIn {
-    fn read(&mut self, buf: &mut [u8]) -> Option<uint> { self.fd.read(buf) }
-    fn eof(&mut self) -> bool { self.fd.eof() }
-}
-
-pub struct StdOut {
-    priv fd: file::FileDesc
-}
-
-impl StdOut {
-    /// Duplicates the specified file descriptor, returning an io::Writer
-    pub fn new(fd: file::fd_t) -> StdOut {
-        StdOut { fd: file::FileDesc::new(fd, false) }
-    }
-}
-
-impl Writer for StdOut {
-    fn write(&mut self, buf: &[u8]) { self.fd.write(buf) }
-    fn flush(&mut self) { self.fd.flush() }
-}
diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs
index 302d7579873..6df9ea6c60a 100644
--- a/src/libstd/io/stdio.rs
+++ b/src/libstd/io/stdio.rs
@@ -126,17 +126,23 @@ fn with_task_stdout(f: &fn(&mut Writer)) {
     use rt::task::Task;
 
     unsafe {
-        // Logging may require scheduling operations, so we can't remove the
-        // task from TLS right now, hence the unsafe borrow. Sad.
-        let task: *mut Task = Local::unsafe_borrow();
+        let task: Option<*mut Task> = Local::try_unsafe_borrow();
+        match task {
+            Some(task) => {
+                match (*task).stdout_handle {
+                    Some(ref mut handle) => f(*handle),
+                    None => {
+                        let handle = ~LineBufferedWriter::new(stdout());
+                        let mut handle = handle as ~Writer;
+                        f(handle);
+                        (*task).stdout_handle = Some(handle);
+                    }
+                }
+            }
 
-        match (*task).stdout_handle {
-            Some(ref mut handle) => f(*handle),
             None => {
-                let handle = stdout();
-                let mut handle = ~LineBufferedWriter::new(handle) as ~Writer;
-                f(handle);
-                (*task).stdout_handle = Some(handle);
+                let mut io = stdout();
+                f(&mut io as &mut Writer);
             }
         }
     }
diff --git a/src/libstd/rt/basic.rs b/src/libstd/rt/basic.rs
index a8f762c4c8f..42ecbf5dc78 100644
--- a/src/libstd/rt/basic.rs
+++ b/src/libstd/rt/basic.rs
@@ -18,6 +18,7 @@ use cast;
 use rt::rtio::{EventLoop, IoFactory, RemoteCallback, PausibleIdleCallback,
                Callback};
 use unstable::sync::Exclusive;
+use io::native;
 use util;
 
 /// This is the only exported function from this module.
@@ -30,7 +31,8 @@ struct BasicLoop {
     idle: Option<*mut BasicPausible>, // only one is allowed
     remotes: ~[(uint, ~Callback)],
     next_remote: uint,
-    messages: Exclusive<~[Message]>
+    messages: Exclusive<~[Message]>,
+    io: ~IoFactory,
 }
 
 enum Message { RunRemote(uint), RemoveRemote(uint) }
@@ -54,6 +56,7 @@ impl BasicLoop {
             next_remote: 0,
             remotes: ~[],
             messages: Exclusive::new(~[]),
+            io: ~native::IoFactory as ~IoFactory,
         }
     }
 
@@ -167,8 +170,9 @@ impl EventLoop for BasicLoop {
         ~BasicRemote::new(self.messages.clone(), id) as ~RemoteCallback
     }
 
-    /// This has no bindings for local I/O
-    fn io<'a>(&'a mut self, _: &fn(&'a mut IoFactory)) {}
+    fn io<'a>(&'a mut self, f: &fn(&'a mut IoFactory)) {
+        f(self.io)
+    }
 }
 
 struct BasicRemote {
diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs
index ca1fd413a56..35fb8baa6ce 100644
--- a/src/libstd/rt/rtio.rs
+++ b/src/libstd/rt/rtio.rs
@@ -78,20 +78,28 @@ pub enum CloseBehavior {
 pub fn with_local_io<T>(f: &fn(&mut IoFactory) -> Option<T>) -> Option<T> {
     use rt::sched::Scheduler;
     use rt::local::Local;
-    use io::{io_error, standard_error, IoUnavailable};
+    use io::native;
 
     unsafe {
-        let sched: *mut Scheduler = Local::unsafe_borrow();
-        let mut io = None;
-        (*sched).event_loop.io(|i| io = Some(i));
-        match io {
-            Some(io) => f(io),
-            None => {
-                io_error::cond.raise(standard_error(IoUnavailable));
-                None
+        // First, attempt to use the local scheduler's I/O services
+        let sched: Option<*mut Scheduler> = Local::try_unsafe_borrow();
+        match sched {
+            Some(sched) => {
+                let mut io = None;
+                (*sched).event_loop.io(|i| io = Some(i));
+                match io {
+                    Some(io) => return f(io),
+                    None => {}
+                }
             }
+            None => {}
         }
     }
+
+    // If we don't have a scheduler or the scheduler doesn't have I/O services,
+    // then fall back to the native I/O services.
+    let mut io = native::IoFactory;
+    f(&mut io as &mut IoFactory)
 }
 
 pub trait IoFactory {
diff --git a/src/libstd/rt/util.rs b/src/libstd/rt/util.rs
index 42546cb3c6a..b5b76f6af66 100644
--- a/src/libstd/rt/util.rs
+++ b/src/libstd/rt/util.rs
@@ -68,25 +68,11 @@ pub fn default_sched_threads() -> uint {
 }
 
 pub fn dumb_println(args: &fmt::Arguments) {
-    use io::native::stdio::stderr;
-    use io::{Writer, io_error, ResourceUnavailable};
-    use rt::task::Task;
-    use rt::local::Local;
-
-    let mut out = stderr();
-    if Local::exists(None::<Task>) {
-        let mut again = true;
-        do io_error::cond.trap(|e| {
-            again = e.kind == ResourceUnavailable;
-        }).inside {
-            while again {
-                again = false;
-                fmt::writeln(&mut out as &mut Writer, args);
-            }
-        }
-    } else {
-        fmt::writeln(&mut out as &mut Writer, args);
-    }
+    use io::native::file::FileDesc;
+    use io;
+    use libc;
+    let mut out = FileDesc::new(libc::STDERR_FILENO, false);
+    fmt::writeln(&mut out as &mut io::Writer, args);
 }
 
 pub fn abort(msg: &str) -> ! {