diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2013-10-17 17:04:51 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2013-10-24 14:21:57 -0700 |
| commit | 4eb53360541baf3e6df36dc0f0766bc7c1c9f8be (patch) | |
| tree | 0b78ce834f9df44297595d66b38883831811b99f /src/libstd/rt/io | |
| parent | 4ce71eaca34526d0e3ee1ebf0658d2a20d388ef2 (diff) | |
| download | rust-4eb53360541baf3e6df36dc0f0766bc7c1c9f8be.tar.gz rust-4eb53360541baf3e6df36dc0f0766bc7c1c9f8be.zip | |
Move as much I/O as possible off of native::io
When uv's TTY I/O is used for the stdio streams, the file descriptors are put into a non-blocking mode. This means that other concurrent writes to the same stream can fail with EAGAIN or EWOULDBLOCK. By all I/O to event-loop I/O, we avoid this error. There is one location which cannot move, which is the runtime's dumb_println function. This was implemented to handle the EAGAIN and EWOULDBLOCK errors and simply retry again and again.
Diffstat (limited to 'src/libstd/rt/io')
| -rw-r--r-- | src/libstd/rt/io/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/io/native/file.rs | 6 | ||||
| -rw-r--r-- | src/libstd/rt/io/stdio.rs | 68 |
3 files changed, 52 insertions, 24 deletions
diff --git a/src/libstd/rt/io/mod.rs b/src/libstd/rt/io/mod.rs index 1a5c197fd52..240210880bf 100644 --- a/src/libstd/rt/io/mod.rs +++ b/src/libstd/rt/io/mod.rs @@ -370,6 +370,7 @@ pub enum IoErrorKind { PathAlreadyExists, PathDoesntExist, MismatchedFileTypeForOperation, + ResourceUnavailable, IoUnavailable, } @@ -392,6 +393,7 @@ impl ToStr for IoErrorKind { PathDoesntExist => ~"PathDoesntExist", MismatchedFileTypeForOperation => ~"MismatchedFileTypeForOperation", IoUnavailable => ~"IoUnavailable", + ResourceUnavailable => ~"ResourceUnavailable", } } } diff --git a/src/libstd/rt/io/native/file.rs b/src/libstd/rt/io/native/file.rs index d6820981181..a2b2289679a 100644 --- a/src/libstd/rt/io/native/file.rs +++ b/src/libstd/rt/io/native/file.rs @@ -21,6 +21,12 @@ fn raise_error() { // XXX: this should probably be a bit more descriptive... let (kind, desc) = match os::errno() as i32 { 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"), }; diff --git a/src/libstd/rt/io/stdio.rs b/src/libstd/rt/io/stdio.rs index 52838425422..294df9a6442 100644 --- a/src/libstd/rt/io/stdio.rs +++ b/src/libstd/rt/io/stdio.rs @@ -8,6 +8,24 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +/*! + +This modules provides bindings to the local event loop's TTY interface, using it +to have synchronous, but non-blocking versions of stdio. These handles can be +inspected for information about terminal dimensions or related information +about the stream or terminal that it is attached to. + +# Example + +```rust +use std::rt::io; + +let mut out = io::stdout(); +out.write(bytes!("Hello, world!")); +``` + +*/ + use fmt; use libc; use option::{Option, Some, None}; @@ -15,13 +33,14 @@ use result::{Ok, Err}; use rt::rtio::{IoFactory, RtioTTY, with_local_io}; use super::{Reader, Writer, io_error}; -/// Creates a new non-blocking handle to the stdin of the current process. -/// -/// See `stdout()` for notes about this function. -pub fn stdin() -> StdReader { +#[fixed_stack_segment] #[inline(never)] +fn tty<T>(fd: libc::c_int, f: &fn(~RtioTTY) -> T) -> T { do with_local_io |io| { - match io.tty_open(libc::STDIN_FILENO, true, false) { - Ok(tty) => Some(StdReader { inner: tty }), + // Always pass in readable as true, otherwise libuv turns our writes + // into blocking writes. We also need to dup the file descriptor because + // the tty will be closed when it's dropped. + match io.tty_open(unsafe { libc::dup(fd) }, true) { + Ok(tty) => Some(f(tty)), Err(e) => { io_error::cond.raise(e); None @@ -30,6 +49,13 @@ pub fn stdin() -> StdReader { }.unwrap() } +/// Creates a new non-blocking handle to the stdin of the current process. +/// +/// See `stdout()` for notes about this function. +pub fn stdin() -> StdReader { + do tty(libc::STDIN_FILENO) |tty| { StdReader { inner: tty } } +} + /// Creates a new non-blocking handle to the stdout of the current process. /// /// Note that this is a fairly expensive operation in that at least one memory @@ -37,30 +63,14 @@ pub fn stdin() -> StdReader { /// task context because the stream returned will be a non-blocking object using /// the local scheduler to perform the I/O. pub fn stdout() -> StdWriter { - do with_local_io |io| { - match io.tty_open(libc::STDOUT_FILENO, false, false) { - Ok(tty) => Some(StdWriter { inner: tty }), - Err(e) => { - io_error::cond.raise(e); - None - } - } - }.unwrap() + do tty(libc::STDOUT_FILENO) |tty| { StdWriter { inner: tty } } } /// Creates a new non-blocking handle to the stderr of the current process. /// /// See `stdout()` for notes about this function. pub fn stderr() -> StdWriter { - do with_local_io |io| { - match io.tty_open(libc::STDERR_FILENO, false, false) { - Ok(tty) => Some(StdWriter { inner: tty }), - Err(e) => { - io_error::cond.raise(e); - None - } - } - }.unwrap() + do tty(libc::STDERR_FILENO) |tty| { StdWriter { inner: tty } } } /// Prints a string to the stdout of the current process. No newline is emitted @@ -115,6 +125,11 @@ impl StdReader { Err(e) => io_error::cond.raise(e), } } + + /// Returns whether this tream is attached to a TTY instance or not. + /// + /// This is similar to libc's isatty() function + pub fn isatty(&self) -> bool { self.inner.isatty() } } impl Reader for StdReader { @@ -170,6 +185,11 @@ impl StdWriter { Err(e) => io_error::cond.raise(e), } } + + /// Returns whether this tream is attached to a TTY instance or not. + /// + /// This is similar to libc's isatty() function + pub fn isatty(&self) -> bool { self.inner.isatty() } } impl Writer for StdWriter { |
