]> git.lizzy.rs Git - rust.git/commitdiff
Implement native::IoFactory
authorAlex Crichton <alex@alexcrichton.com>
Tue, 12 Nov 2013 22:38:28 +0000 (14:38 -0800)
committerAlex Crichton <alex@alexcrichton.com>
Thu, 14 Nov 2013 02:34:59 +0000 (18:34 -0800)
This commit re-organizes the io::native module slightly in order to have a
working implementation of rtio::IoFactory which uses native implementations. The
goal is to seamlessly multiplex among libuv/native implementations wherever
necessary.

Right now most of the native I/O is unimplemented, but we have existing bindings
for file descriptors and processes which have been hooked up. What this means is
that you can now invoke println!() from libstd with no local task, no local
scheduler, and even without libuv.

There's still plenty of work to do on the native I/O factory, but this is the
first steps into making it an official portion of the standard library. I don't
expect anyone to reach into io::native directly, but rather only std::io
primitives will be used. Each std::io interface seamlessly falls back onto the
native I/O implementation if the local scheduler doesn't have a libuv one
(hurray trait ojects!)

13 files changed:
src/librustuv/macros.rs
src/libstd/io/mod.rs
src/libstd/io/native/file.rs
src/libstd/io/native/mod.rs [new file with mode: 0644]
src/libstd/io/native/process.rs
src/libstd/io/native/stdio.rs [deleted file]
src/libstd/io/stdio.rs
src/libstd/rt/basic.rs
src/libstd/rt/rtio.rs
src/libstd/rt/util.rs
src/test/bench/shootout-k-nucleotide-pipes.rs
src/test/run-pass/native-print-no-runtime.rs [new file with mode: 0644]
src/test/run-pass/native-print-no-uv.rs [new file with mode: 0644]

index 01f2bfd648cd587250755df246e9d6160bd905fe..71744c717d4bddd5a3897f6ebeba298070c2830b 100644 (file)
@@ -33,9 +33,9 @@ macro_rules! get_handle_to_current_scheduler(
 )
 
 pub fn dumb_println(args: &fmt::Arguments) {
-    use std::io::native::stdio::stderr;
-    use std::io::Writer;
-
-    let mut out = stderr();
-    fmt::writeln(&mut out as &mut Writer, args);
+    use std::io::native::file::FileDesc;
+    use std::io;
+    use std::libc;
+    let mut out = FileDesc::new(libc::STDERR_FILENO, false);
+    fmt::writeln(&mut out as &mut io::Writer, args);
 }
index 8858d0a14adf2122f3643dd781338d0bdb377326..2aed3bdb8496fddeefdeb79f786d1a442fe7146d 100644 (file)
 /// 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;
index 0f1a64edb607286660fe8d863e4dd5dcaaffcd6e..5e39460ba6a4dc9e2361be9a85b09759f3716fed 100644 (file)
 
 #[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 @@ fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
             }
         };
         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 @@ fn write(&mut self, buf: &[u8]) {
             }
         };
         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 @@ fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
             }
         };
         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 @@ fn write(&mut self, buf: &[u8]) {
             }
         };
         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 @@ fn drop(&mut self) {
 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 @@ fn test_file_desc() {
             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 @@ fn test_file_desc() {
                 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 @@ fn test_cfile() {
             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 (file)
index 0000000..d7aa6c1
--- /dev/null
@@ -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())
+    }
+}
index de03ac1c07d509a66df7a9fa8f3fc466e10c3683..71c6ce78a1ed1ded2c3764bf79c742cb06dd615c 100644 (file)
@@ -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 @@ pub fn new(prog: &str, args: &[~str], env: Option<~[(~str, ~str)]>,
             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 @@ pub fn signal(&mut self, signum: int) -> Result<(), io::IoError> {
         #[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 @@ unsafe fn killpid(pid: pid_t, signal: int) -> Result<(), io::IoError> {
 
 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 (file)
index 68748ab..0000000
+++ /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() }
-}
index 302d7579873889e42466ca4236fcba79e868ba33..6df9ea6c60a6d6f1ca04e8dd93af3fd6379a7204 100644 (file)
@@ -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);
             }
         }
     }
index a8f762c4c8fc127934b027eac2d22127cf1ddcb4..42ecbf5dc785640421ee64e94757ae0214206d99 100644 (file)
@@ -18,6 +18,7 @@
 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 @@ fn new() -> BasicLoop {
             next_remote: 0,
             remotes: ~[],
             messages: Exclusive::new(~[]),
+            io: ~native::IoFactory as ~IoFactory,
         }
     }
 
@@ -167,8 +170,9 @@ fn remote_callback(&mut self, f: ~Callback) -> ~RemoteCallback {
         ~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 {
index ca1fd413a5628d397a58e8cdae13e73bb6e308cc..35fb8baa6ce688db8dc9b65ee56c2f9059352ce0 100644 (file)
@@ -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 {
index 42546cb3c6a99448c5755c1b7bb9e233dc274d16..b5b76f6af66061d616076cab6b4f5bb8a93beda2 100644 (file)
@@ -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) -> ! {
index 0937c71f91f4c0b5d180d75aab387b225b463ce4..5cc8e161f5ffde0236c80f6973a38e9af7391838 100644 (file)
@@ -154,7 +154,7 @@ fn make_sequence_processor(sz: uint,
 // given a FASTA file on stdin, process sequence THREE
 fn main() {
     use std::io::Reader;
-    use std::io::native::stdio;
+    use std::io::stdio;
     use std::io::mem::MemReader;
     use std::io::buffered::BufferedReader;
 
diff --git a/src/test/run-pass/native-print-no-runtime.rs b/src/test/run-pass/native-print-no-runtime.rs
new file mode 100644 (file)
index 0000000..b468b53
--- /dev/null
@@ -0,0 +1,19 @@
+// 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.
+
+// xfail-fast
+
+#[no_uv];
+
+#[start]
+fn main(_: int, _: **u8) -> int {
+    println!("hello");
+    0
+}
diff --git a/src/test/run-pass/native-print-no-uv.rs b/src/test/run-pass/native-print-no-uv.rs
new file mode 100644 (file)
index 0000000..d3b6d60
--- /dev/null
@@ -0,0 +1,17 @@
+// 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.
+
+// xfail-fast
+
+#[no_uv];
+
+fn main() {
+    println!("hello");
+}